diff --git a/.github/workflows/daily-scan.yml b/.github/workflows/daily-scan.yml index 7c93067..27b6234 100644 --- a/.github/workflows/daily-scan.yml +++ b/.github/workflows/daily-scan.yml @@ -73,7 +73,7 @@ jobs: id: high_scan uses: ./.github/actions/image_scan with: - image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-node:v0.3.0" + image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-node:v0.4.0" severity: 'CRITICAL,HIGH' # TODO: Update image to public once available @@ -82,7 +82,7 @@ jobs: id: low_scan uses: ./.github/actions/image_scan with: - image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-node:v0.3.0" + image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-node:v0.4.0" severity: 'MEDIUM,LOW,UNKNOWN' - name: Configure AWS Credentials for emitting metrics diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index cfc2b24..ba64e42 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -14,6 +14,7 @@ env: RELEASE_PRIVATE_REPOSITORY: 020628701572.dkr.ecr.us-west-2.amazonaws.com/adot-autoinstrumentation-node RELEASE_PRIVATE_REGISTRY: 020628701572.dkr.ecr.us-west-2.amazonaws.com PACKAGE_NAME: aws-distro-opentelemetry-node-autoinstrumentation + ARTIFACT_NAME: aws-aws-distro-opentelemetry-node-autoinstrumentation-${{ github.event.inputs.version }}.tgz permissions: id-token: write @@ -86,6 +87,11 @@ jobs: tags: | ${{ env.RELEASE_PRIVATE_REPOSITORY }}:v${{ github.event.inputs.version }} + - name: Get SHA256 checksum of release artifact + id: get_sha256 + run: | + shasum -a 256 aws-distro-opentelemetry-node-autoinstrumentation/${{ env.ARTIFACT_NAME }} | sed "s|aws-distro-opentelemetry-node-autoinstrumentation/||" > ${{ env.ARTIFACT_NAME }}.sha256 + # Publish to GitHub releases - name: Create GH release id: create_release @@ -96,7 +102,8 @@ jobs: --title "Release v${{ github.event.inputs.version }}" \ --draft \ "v${{ github.event.inputs.version }}" \ - aws-distro-opentelemetry-node-autoinstrumentation/aws-aws-distro-opentelemetry-node-autoinstrumentation-${{ github.event.inputs.version }}.tgz + aws-distro-opentelemetry-node-autoinstrumentation/${{ env.ARTIFACT_NAME }} \ + ${{ env.ARTIFACT_NAME }}.sha256 # Publish to npm - name: Publish to npm @@ -104,26 +111,5 @@ jobs: NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} NPM_CONFIG_PROVENANCE: true run: npx lerna publish from-package --no-push --no-private --no-git-tag-version --no-verify-access --yes - - - name: Get SHA256 checksum of wheel file - id: get_sha256 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - checksum=$(shasum -a 256 aws-distro-opentelemetry-node-autoinstrumentation/aws-aws-distro-opentelemetry-node-autoinstrumentation-${{ github.event.inputs.version }}.tgz | awk '{ print $1 }') - echo "CHECKSUM=$checksum" >> $GITHUB_OUTPUT - - - name: Append checksum and update version - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - echo "aws-aws-distro-opentelemetry-node-autoinstrumentation-${{ github.event.inputs.version }}.tgz ${{ steps.get_sha256.outputs.CHECKSUM }}" >> checksum.txt - echo "${{ github.event.inputs.version }}" > version.txt - - git config --local user.email "github-actions[bot]@users.noreply.github.com" - git config --local user.name "GitHub Action Release Workflow" - git add checksum.txt version.txt - git commit -m "Update latest version and append checksum" - git push \ No newline at end of file diff --git a/.github/workflows/release-lambda.yml b/.github/workflows/release-lambda.yml index 5dbcd82..411545d 100644 --- a/.github/workflows/release-lambda.yml +++ b/.github/workflows/release-lambda.yml @@ -39,7 +39,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: NPM Clean Install # https://docs.npmjs.com/cli/v10/commands/npm-ci run: npm ci @@ -100,7 +100,7 @@ jobs: aws lambda publish-layer-version \ --layer-name ${{ env.LAYER_NAME }} \ --content S3Bucket=${{ env.BUCKET_NAME }},S3Key=layer.zip \ - --compatible-runtimes nodejs18.x nodejs20.x \ + --compatible-runtimes nodejs18.x nodejs20.x nodejs22.x \ --compatible-architectures "arm64" "x86_64" \ --license-info "Apache-2.0" \ --description "AWS Distro of OpenTelemetry Lambda Layer for NodeJs Runtime" \ @@ -186,16 +186,6 @@ jobs: with: name: layer.tf path: layer.tf - - name: Commit changes - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - mv layer.tf lambda-layer/terraform/lambda/ - git add lambda-layer/terraform/lambda/layer.tf - git commit -m "Update Lambda layer ARNs for releasing" || echo "No changes to commit" - git push create-release: runs-on: ubuntu-latest needs: generate-release-note @@ -207,23 +197,12 @@ jobs: echo "COMMIT_SHA=${GITHUB_SHA}" >> $GITHUB_ENV SHORT_SHA=$(echo $GITHUB_SHA | cut -c1-7) echo "SHORT_SHA=${SHORT_SHA}" >> $GITHUB_ENV - - name: Create Tag - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - TAG_NAME="lambda-${SHORT_SHA}" - git tag -a "$TAG_NAME" -m "Release Lambda layer based on commit $TAG_NAME" - git push origin "$TAG_NAME" - echo "TAG_NAME=${TAG_NAME}" >> $GITHUB_ENV - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Create Release id: create_release uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - tag_name: ${{ env.TAG_NAME }} release_name: "Release AWSOpenTelemetryDistroPython Lambda Layer" body_path: lambda-layer/terraform/lambda/layer.tf draft: true diff --git a/Dockerfile b/Dockerfile index 33e0b8b..bfc1705 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ RUN npm install aws-aws-distro-opentelemetry-node-autoinstrumentation-$(node -p RUN npm install # Stage 2: Build the cp-utility binary -FROM public.ecr.aws/docker/library/rust:1.75 as builder +FROM public.ecr.aws/docker/library/rust:1.81 as builder WORKDIR /usr/src/cp-utility COPY ./tools/cp-utility . diff --git a/README.md b/README.md index bd50333..60a98d0 100644 --- a/README.md +++ b/README.md @@ -44,3 +44,9 @@ This project ensures compatibility with the following supported NodeJS versions: ### Note on Amazon CloudWatch Application Signals [Amazon CloudWatch Application Signals](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Application-Monitoring-Sections.html) components are designed to seamlessly work with all library instrumentations offered by [OpenTelemetry NodeJS auto-instrumentation](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/metapackages/auto-instrumentations-node/README.md). While upstream OpenTelemetry NodeJS instrumentations are in beta, Application Signals components are stable, production ready and have also been tested for popular libraries/frameworks such as `ExpressJS, AWS SDK for JavaScript V3, and others`. We will prioritize backward compatibility for Application Signals components, striving to ensure that they remain functional even in the face of potential breaking changes introduced by OpenTelemetry upstream libraries. Please [raise an issue](https://github.com/aws-observability/aws-otel-js-instrumentation/blob/main/CONTRIBUTING.md#reporting-bugsfeature-requests) if you notice Application Signals doesn't work for a particular OpenTelemetry supported library. + +## Checksum Verification + +Artifacts released will include a `.sha256` file for checksum verification starting from v0.4.0 +To verify, run the command `shasum -a 256 -c .sha256` +It should return the output `: OK` if the validation is successful \ No newline at end of file diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/.eslintignore b/aws-distro-opentelemetry-node-autoinstrumentation/.eslintignore index 34c98dc..8cbd431 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/.eslintignore +++ b/aws-distro-opentelemetry-node-autoinstrumentation/.eslintignore @@ -1,4 +1,5 @@ build node_modules .eslintrc.js -version.ts \ No newline at end of file +version.ts +src/third-party \ No newline at end of file diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/package.json b/aws-distro-opentelemetry-node-autoinstrumentation/package.json index c63a62b..78412fa 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/package.json +++ b/aws-distro-opentelemetry-node-autoinstrumentation/package.json @@ -1,6 +1,6 @@ { "name": "@aws/aws-distro-opentelemetry-node-autoinstrumentation", - "version": "0.3.0-dev0", + "version": "0.4.0-dev0", "description": "This package provides Amazon Web Services distribution of the OpenTelemetry Node Instrumentation, which allows for auto-instrumentation of NodeJS applications.", "author": { "name": "Amazon Web Services", @@ -31,9 +31,18 @@ "prepublishOnly": "npm run compile", "tdd": "yarn test -- --watch-extensions ts --watch", "test": "nyc ts-mocha --timeout 10000 -p tsconfig.json --require '@opentelemetry/contrib-test-utils' 'test/**/*.ts'", - "test:coverage": "nyc --all --check-coverage --functions 95 --lines 95 ts-mocha --timeout 10000 -p tsconfig.json --require '@opentelemetry/contrib-test-utils' 'test/**/*.ts'", + "test:coverage": "nyc --check-coverage --functions 95 --lines 95 ts-mocha --timeout 10000 -p tsconfig.json --require '@opentelemetry/contrib-test-utils' 'test/**/*.ts'", "watch": "tsc -w" }, + "nyc": { + "all": true, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "src/third-party/**/*.ts" + ] + }, "bugs": { "url": "https://github.com/aws-observability/aws-otel-js-instrumentation/issues" }, @@ -64,8 +73,11 @@ "@aws-sdk/client-bedrock-agent-runtime": "3.632.0", "@aws-sdk/client-bedrock-runtime": "3.632.0", "@aws-sdk/client-kinesis": "3.632.0", + "@aws-sdk/client-lambda": "^3.632.0", "@aws-sdk/client-s3": "3.632.0", - "@aws-sdk/client-sqs": "3.632.0", + "@aws-sdk/client-secrets-manager": "^3.632.0", + "@aws-sdk/client-sfn": "^3.632.0", + "@aws-sdk/client-sns": "^3.632.0", "@opentelemetry/contrib-test-utils": "0.41.0", "@types/mocha": "7.0.2", "@types/node": "18.6.5", @@ -99,57 +111,6 @@ "@opentelemetry/sdk-trace-base": "1.26.0", "@opentelemetry/semantic-conventions": "1.27.0" }, - "overrides": { - "@opentelemetry/auto-instrumentations-node": { - "@opentelemetry/instrumentation": "0.53.0", - "@opentelemetry/instrumentation-amqplib": "0.42.0", - "@opentelemetry/instrumentation-aws-lambda": "0.44.0", - "@opentelemetry/instrumentation-aws-sdk": "0.44.0", - "@opentelemetry/instrumentation-bunyan": "0.41.0", - "@opentelemetry/instrumentation-cassandra-driver": "0.41.0", - "@opentelemetry/instrumentation-connect": "0.39.0", - "@opentelemetry/instrumentation-cucumber": "0.9.0", - "@opentelemetry/instrumentation-dataloader": "0.12.0", - "@opentelemetry/instrumentation-dns": "0.39.0", - "@opentelemetry/instrumentation-express": "0.42.0", - "@opentelemetry/instrumentation-fastify": "0.39.0", - "@opentelemetry/instrumentation-fs": "0.15.0", - "@opentelemetry/instrumentation-generic-pool": "0.39.0", - "@opentelemetry/instrumentation-graphql": "0.43.0", - "@opentelemetry/instrumentation-grpc": "0.53.0", - "@opentelemetry/instrumentation-hapi": "0.41.0", - "@opentelemetry/instrumentation-http": "0.53.0", - "@opentelemetry/instrumentation-ioredis": "0.43.0", - "@opentelemetry/instrumentation-kafkajs": "0.3.0", - "@opentelemetry/instrumentation-knex": "0.40.0", - "@opentelemetry/instrumentation-koa": "0.43.0", - "@opentelemetry/instrumentation-lru-memoizer": "0.40.0", - "@opentelemetry/instrumentation-memcached": "0.39.0", - "@opentelemetry/instrumentation-mongodb": "0.47.0", - "@opentelemetry/instrumentation-mongoose": "0.42.0", - "@opentelemetry/instrumentation-mysql": "0.41.0", - "@opentelemetry/instrumentation-mysql2": "0.41.0", - "@opentelemetry/instrumentation-nestjs-core": "0.40.0", - "@opentelemetry/instrumentation-net": "0.39.0", - "@opentelemetry/instrumentation-pg": "0.44.0", - "@opentelemetry/instrumentation-pino": "0.42.0", - "@opentelemetry/instrumentation-redis": "0.42.0", - "@opentelemetry/instrumentation-redis-4": "0.42.0", - "@opentelemetry/instrumentation-restify": "0.41.0", - "@opentelemetry/instrumentation-router": "0.40.0", - "@opentelemetry/instrumentation-socket.io": "0.42.0", - "@opentelemetry/instrumentation-tedious": "0.14.0", - "@opentelemetry/instrumentation-undici": "0.6.0", - "@opentelemetry/instrumentation-winston": "0.40.0", - "@opentelemetry/resource-detector-alibaba-cloud": "0.29.1", - "@opentelemetry/resource-detector-aws": "1.6.1", - "@opentelemetry/resource-detector-azure": "0.2.11", - "@opentelemetry/resource-detector-container": "0.4.1", - "@opentelemetry/resource-detector-gcp": "0.29.11", - "@opentelemetry/resources": "1.26.0", - "@opentelemetry/sdk-node": "0.53.0" - } - }, "files": [ "build/src/**/*.js", "build/src/**/*.js.map", diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-attribute-keys.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-attribute-keys.ts index e237827..46150b7 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-attribute-keys.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-attribute-keys.ts @@ -14,6 +14,7 @@ export const AWS_ATTRIBUTE_KEYS: { [key: string]: string } = { AWS_REMOTE_RESOURCE_IDENTIFIER: 'aws.remote.resource.identifier', AWS_SDK_DESCENDANT: 'aws.sdk.descendant', AWS_CONSUMER_PARENT_SPAN_KIND: 'aws.consumer.parent.span.kind', + AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER: 'aws.remote.resource.cfn.primary.identifier', AWS_REMOTE_TARGET: 'aws.remote.target', AWS_REMOTE_DB_USER: 'aws.remote.db.user', @@ -35,4 +36,12 @@ export const AWS_ATTRIBUTE_KEYS: { [key: string]: string } = { AWS_BEDROCK_KNOWLEDGE_BASE_ID: 'aws.bedrock.knowledge_base.id', AWS_BEDROCK_AGENT_ID: 'aws.bedrock.agent.id', AWS_BEDROCK_GUARDRAIL_ID: 'aws.bedrock.guardrail.id', + AWS_BEDROCK_GUARDRAIL_ARN: 'aws.bedrock.guardrail.arn', + AWS_SNS_TOPIC_ARN: 'aws.sns.topic.arn', + AWS_SECRETSMANAGER_SECRET_ARN: 'aws.secretsmanager.secret.arn', + AWS_STEPFUNCTIONS_STATEMACHINE_ARN: 'aws.stepfunctions.state_machine.arn', + AWS_STEPFUNCTIONS_ACTIVITY_ARN: 'aws.stepfunctions.activity.arn', + AWS_LAMBDA_FUNCTION_NAME: 'aws.lambda.function.name', + AWS_LAMBDA_RESOURCE_MAPPING_ID: 'aws.lambda.resource_mapping.id', + AWS_LAMBDA_FUNCTION_ARN: 'aws.lambda.function.arn', }; diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-metric-attribute-generator.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-metric-attribute-generator.ts index e58b600..ae318a3 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-metric-attribute-generator.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-metric-attribute-generator.ts @@ -55,6 +55,10 @@ const NORMALIZED_DYNAMO_DB_SERVICE_NAME: string = 'AWS::DynamoDB'; const NORMALIZED_KINESIS_SERVICE_NAME: string = 'AWS::Kinesis'; const NORMALIZED_S3_SERVICE_NAME: string = 'AWS::S3'; const NORMALIZED_SQS_SERVICE_NAME: string = 'AWS::SQS'; +const NORMALIZED_SNS_SERVICE_NAME: string = 'AWS::SNS'; +const NORMALIZED_SECRETSMANAGER_SERVICE_NAME = 'AWS::SecretsManager'; +const NORMALIZED_STEPFUNCTIONS_SERVICE_NAME = 'AWS::StepFunctions'; +const NORMALIZED_LAMBDA_SERVICE_NAME = 'AWS::Lambda'; const NORMALIZED_BEDROCK_SERVICE_NAME: string = 'AWS::Bedrock'; const NORMALIZED_BEDROCK_RUNTIME_SERVICE_NAME: string = 'AWS::BedrockRuntime'; @@ -330,6 +334,8 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator { BedrockAgent: NORMALIZED_BEDROCK_SERVICE_NAME, BedrockAgentRuntime: NORMALIZED_BEDROCK_SERVICE_NAME, BedrockRuntime: NORMALIZED_BEDROCK_RUNTIME_SERVICE_NAME, + SecretsManager: NORMALIZED_SECRETSMANAGER_SERVICE_NAME, + SFN: NORMALIZED_STEPFUNCTIONS_SERVICE_NAME, }; return awsSdkServiceMapping[serviceName] || 'AWS::' + serviceName; } @@ -350,6 +356,7 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator { private static setRemoteResourceTypeAndIdentifier(span: ReadableSpan, attributes: Attributes): void { let remoteResourceType: AttributeValue | undefined; let remoteResourceIdentifier: AttributeValue | undefined; + let cloudFormationIdentifier: AttributeValue | undefined; if (AwsSpanProcessingUtil.isAwsSDKSpan(span)) { const awsTableNames: AttributeValue | undefined = span.attributes[AWS_ATTRIBUTE_KEYS.AWS_DYNAMODB_TABLE_NAMES]; @@ -370,16 +377,56 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator { remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( span.attributes[AWS_ATTRIBUTE_KEYS.AWS_S3_BUCKET] ); + } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN)) { + const snsArn = span.attributes[AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN]; + + remoteResourceType = NORMALIZED_SNS_SERVICE_NAME + '::Topic'; + remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( + this.extractResourceNameFromArn(snsArn) + ); + cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(snsArn); + } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN)) { + const secretsArn = span.attributes[AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN]; + + remoteResourceType = NORMALIZED_SECRETSMANAGER_SERVICE_NAME + '::Secret'; + remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( + this.extractResourceNameFromArn(secretsArn) + ); + cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(secretsArn); + } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN)) { + const stateMachineArn = span.attributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN]; + + remoteResourceType = NORMALIZED_STEPFUNCTIONS_SERVICE_NAME + '::StateMachine'; + remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( + this.extractResourceNameFromArn(stateMachineArn) + ); + cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(stateMachineArn); + } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN)) { + const activityArn = span.attributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN]; + + remoteResourceType = NORMALIZED_STEPFUNCTIONS_SERVICE_NAME + '::Activity'; + remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( + this.extractResourceNameFromArn(activityArn) + ); + cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(activityArn); + } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID)) { + remoteResourceType = NORMALIZED_LAMBDA_SERVICE_NAME + '::EventSourceMapping'; + remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( + span.attributes[AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID] + ); } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_NAME)) { remoteResourceType = NORMALIZED_SQS_SERVICE_NAME + '::Queue'; remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( span.attributes[AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_NAME] ); } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_URL)) { - remoteResourceType = NORMALIZED_SQS_SERVICE_NAME + '::Queue'; - remoteResourceIdentifier = SqsUrlParser.getQueueName( - AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_URL]) + const sqsQueueUrl = AwsMetricAttributeGenerator.escapeDelimiters( + span.attributes[AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_URL] ); + + remoteResourceType = NORMALIZED_SQS_SERVICE_NAME + '::Queue'; + remoteResourceIdentifier = SqsUrlParser.getQueueName(sqsQueueUrl); + cloudFormationIdentifier = sqsQueueUrl; } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID)) { remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::Agent'; remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( @@ -390,11 +437,17 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator { remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( span.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID] ); + cloudFormationIdentifier = `${AwsMetricAttributeGenerator.escapeDelimiters( + span.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID] + )}|${remoteResourceIdentifier}`; } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID)) { remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::Guardrail'; remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( span.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID] ); + cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( + span.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ARN] + ); } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID)) { remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::KnowledgeBase'; remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( @@ -414,6 +467,14 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator { if (remoteResourceType !== undefined && remoteResourceIdentifier !== undefined) { attributes[AWS_ATTRIBUTE_KEYS.AWS_REMOTE_RESOURCE_TYPE] = remoteResourceType; attributes[AWS_ATTRIBUTE_KEYS.AWS_REMOTE_RESOURCE_IDENTIFIER] = remoteResourceIdentifier; + + if (AwsSpanProcessingUtil.isAwsSDKSpan(span)) { + if (cloudFormationIdentifier === undefined) { + cloudFormationIdentifier = remoteResourceIdentifier; + } + + attributes[AWS_ATTRIBUTE_KEYS.AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER] = cloudFormationIdentifier; + } } } @@ -532,6 +593,16 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator { return input.split('^').join('^^').split('|').join('^|'); } + // Extracts the name of the resource from an arn + private static extractResourceNameFromArn(attribute: AttributeValue | undefined): string | undefined { + if (typeof attribute === 'string' && attribute.startsWith('arn:aws:')) { + const split = attribute.split(':'); + return split[split.length - 1]; + } + + return undefined; + } + /** Span kind is needed for differentiating metrics in the EMF exporter */ private static setSpanKindForService(span: ReadableSpan, attributes: Attributes): void { let spanKind: string = SpanKind[span.kind]; diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-span-processing-util.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-span-processing-util.ts index 46c8a6f..6ad9f8c 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-span-processing-util.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-span-processing-util.ts @@ -47,6 +47,12 @@ export class AwsSpanProcessingUtil { // TODO: Use Semantic Conventions once upgraded static GEN_AI_REQUEST_MODEL: string = 'gen_ai.request.model'; static GEN_AI_SYSTEM: string = 'gen_ai.system'; + static GEN_AI_REQUEST_MAX_TOKENS: string = 'gen_ai.request.max_tokens'; + static GEN_AI_REQUEST_TEMPERATURE: string = 'gen_ai.request.temperature'; + static GEN_AI_REQUEST_TOP_P: string = 'gen_ai.request.top_p'; + static GEN_AI_RESPONSE_FINISH_REASONS: string = 'gen_ai.response.finish_reasons'; + static GEN_AI_USAGE_INPUT_TOKENS: string = 'gen_ai.usage.input_tokens'; + static GEN_AI_USAGE_OUTPUT_TOKENS: string = 'gen_ai.usage.output_tokens'; static getDialectKeywords(): string[] { return SQL_DIALECT_KEYWORDS_JSON.keywords; diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/bedrock.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/bedrock.ts index ffeafac..68fa3fe 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/bedrock.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/bedrock.ts @@ -15,8 +15,9 @@ const AGENT_ID: string = 'agentId'; const KNOWLEDGE_BASE_ID: string = 'knowledgeBaseId'; const DATA_SOURCE_ID: string = 'dataSourceId'; const GUARDRAIL_ID: string = 'guardrailId'; +const GUARDRAIL_ARN: string = 'guardrailArn'; const MODEL_ID: string = 'modelId'; -const AWS_BEDROCK_SYSTEM: string = 'aws_bedrock'; +const AWS_BEDROCK_SYSTEM: string = 'aws.bedrock'; const AGENT_OPERATIONS = [ 'CreateAgentActionGroup', @@ -60,6 +61,7 @@ const knowledgeBaseOperationAttributeKeyMapping = { }; const dataSourceOperationAttributeKeyMapping = { [AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]: DATA_SOURCE_ID, + [AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]: KNOWLEDGE_BASE_ID, }; // This map allows us to get all relevant attribute key mappings for a given operation @@ -182,10 +184,15 @@ export class BedrockServiceExtension implements ServiceExtension { }; } responseHook(response: NormalizedResponse, span: Span, tracer: Tracer, config: AwsSdkInstrumentationConfig): void { - const guardrail_id = response.data[GUARDRAIL_ID]; + const guardrailId = response.data[GUARDRAIL_ID]; + const guardrailArn = response.data[GUARDRAIL_ARN]; + + if (guardrailId) { + span.setAttribute(AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID, guardrailId); + } - if (guardrail_id) { - span.setAttribute(AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID, guardrail_id); + if (guardrailArn) { + span.setAttribute(AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ARN, guardrailArn); } } } @@ -211,6 +218,108 @@ export class BedrockRuntimeServiceExtension implements ServiceExtension { spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL] = modelId; } + if (request.commandInput?.body) { + const requestBody = JSON.parse(request.commandInput.body); + if (modelId.includes('amazon.titan')) { + if (requestBody.textGenerationConfig?.temperature !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE] = + requestBody.textGenerationConfig.temperature; + } + if (requestBody.textGenerationConfig?.topP !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P] = requestBody.textGenerationConfig.topP; + } + if (requestBody.textGenerationConfig?.maxTokenCount !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS] = + requestBody.textGenerationConfig.maxTokenCount; + } + } else if (modelId.includes('amazon.nova')) { + if (requestBody.inferenceConfig?.temperature !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE] = requestBody.inferenceConfig.temperature; + } + if (requestBody.inferenceConfig?.top_p !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P] = requestBody.inferenceConfig.top_p; + } + if (requestBody.inferenceConfig?.max_new_tokens !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS] = requestBody.inferenceConfig.max_new_tokens; + } + } else if (modelId.includes('anthropic.claude')) { + if (requestBody.max_tokens !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS] = requestBody.max_tokens; + } + if (requestBody.temperature !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE] = requestBody.temperature; + } + if (requestBody.top_p !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P] = requestBody.top_p; + } + } else if (modelId.includes('meta.llama')) { + if (requestBody.max_gen_len !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS] = requestBody.max_gen_len; + } + if (requestBody.temperature !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE] = requestBody.temperature; + } + if (requestBody.top_p !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P] = requestBody.top_p; + } + } else if (modelId.includes('cohere.command-r')) { + if (requestBody.max_tokens !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS] = requestBody.max_tokens; + } + if (requestBody.temperature !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE] = requestBody.temperature; + } + if (requestBody.p !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P] = requestBody.p; + } + if (requestBody.message !== undefined) { + // NOTE: We approximate the token count since this value is not directly available in the body + // According to Bedrock docs they use (total_chars / 6) to approximate token count for pricing. + // https://docs.aws.amazon.com/bedrock/latest/userguide/model-customization-prepare.html + spanAttributes[AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS] = Math.ceil(requestBody.message.length / 6); + } + } else if (modelId.includes('cohere.command')) { + if (requestBody.max_tokens !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS] = requestBody.max_tokens; + } + if (requestBody.temperature !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE] = requestBody.temperature; + } + if (requestBody.p !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P] = requestBody.p; + } + if (requestBody.prompt !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS] = Math.ceil(requestBody.prompt.length / 6); + } + } else if (modelId.includes('ai21.jamba')) { + if (requestBody.max_tokens !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS] = requestBody.max_tokens; + } + if (requestBody.temperature !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE] = requestBody.temperature; + } + if (requestBody.top_p !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P] = requestBody.top_p; + } + } else if (modelId.includes('mistral')) { + if (requestBody.prompt !== undefined) { + // NOTE: We approximate the token count since this value is not directly available in the body + // According to Bedrock docs they use (total_chars / 6) to approximate token count for pricing. + // https://docs.aws.amazon.com/bedrock/latest/userguide/model-customization-prepare.html + spanAttributes[AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS] = Math.ceil(requestBody.prompt.length / 6); + } + if (requestBody.max_tokens !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS] = requestBody.max_tokens; + } + if (requestBody.temperature !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE] = requestBody.temperature; + } + if (requestBody.top_p !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P] = requestBody.top_p; + } + } + } + return { isIncoming, spanAttributes, @@ -218,4 +327,109 @@ export class BedrockRuntimeServiceExtension implements ServiceExtension { spanName, }; } + + responseHook(response: NormalizedResponse, span: Span, tracer: Tracer, config: AwsSdkInstrumentationConfig): void { + const currentModelId = response.request.commandInput?.modelId; + if (response.data?.body) { + const decodedResponseBody = new TextDecoder().decode(response.data.body); + const responseBody = JSON.parse(decodedResponseBody); + if (currentModelId.includes('amazon.titan')) { + if (responseBody.inputTextTokenCount !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS, responseBody.inputTextTokenCount); + } + if (responseBody.results?.[0]?.tokenCount !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS, responseBody.results[0].tokenCount); + } + if (responseBody.results?.[0]?.completionReason !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS, [ + responseBody.results[0].completionReason, + ]); + } + } else if (currentModelId.includes('amazon.nova')) { + if (responseBody.usage !== undefined) { + if (responseBody.usage.inputTokens !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS, responseBody.usage.inputTokens); + } + if (responseBody.usage.outputTokens !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS, responseBody.usage.outputTokens); + } + } + if (responseBody.stopReason !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS, [responseBody.stopReason]); + } + } else if (currentModelId.includes('anthropic.claude')) { + if (responseBody.usage?.input_tokens !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS, responseBody.usage.input_tokens); + } + if (responseBody.usage?.output_tokens !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS, responseBody.usage.output_tokens); + } + if (responseBody.stop_reason !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS, [responseBody.stop_reason]); + } + } else if (currentModelId.includes('meta.llama')) { + if (responseBody.prompt_token_count !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS, responseBody.prompt_token_count); + } + if (responseBody.generation_token_count !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS, responseBody.generation_token_count); + } + if (responseBody.stop_reason !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS, [responseBody.stop_reason]); + } + } else if (currentModelId.includes('cohere.command-r')) { + if (responseBody.text !== undefined) { + // NOTE: We approximate the token count since this value is not directly available in the body + // According to Bedrock docs they use (total_chars / 6) to approximate token count for pricing. + // https://docs.aws.amazon.com/bedrock/latest/userguide/model-customization-prepare.html + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS, Math.ceil(responseBody.text.length / 6)); + } + if (responseBody.finish_reason !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS, [responseBody.finish_reason]); + } + } else if (currentModelId.includes('cohere.command')) { + if (responseBody.generations?.[0]?.text !== undefined) { + span.setAttribute( + AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS, + // NOTE: We approximate the token count since this value is not directly available in the body + // According to Bedrock docs they use (total_chars / 6) to approximate token count for pricing. + // https://docs.aws.amazon.com/bedrock/latest/userguide/model-customization-prepare.html + Math.ceil(responseBody.generations[0].text.length / 6) + ); + } + if (responseBody.generations?.[0]?.finish_reason !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS, [ + responseBody.generations[0].finish_reason, + ]); + } + } else if (currentModelId.includes('ai21.jamba')) { + if (responseBody.usage?.prompt_tokens !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS, responseBody.usage.prompt_tokens); + } + if (responseBody.usage?.completion_tokens !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS, responseBody.usage.completion_tokens); + } + if (responseBody.choices?.[0]?.finish_reason !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS, [ + responseBody.choices[0].finish_reason, + ]); + } + } else if (currentModelId.includes('mistral')) { + if (responseBody.outputs?.[0]?.text !== undefined) { + span.setAttribute( + AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS, + // NOTE: We approximate the token count since this value is not directly available in the body + // According to Bedrock docs they use (total_chars / 6) to approximate token count for pricing. + // https://docs.aws.amazon.com/bedrock/latest/userguide/model-customization-prepare.html + Math.ceil(responseBody.outputs[0].text.length / 6) + ); + } + if (responseBody.outputs?.[0]?.stop_reason !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS, [ + responseBody.outputs[0].stop_reason, + ]); + } + } + } + } } diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/secretsmanager.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/secretsmanager.ts new file mode 100644 index 0000000..bf78434 --- /dev/null +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/secretsmanager.ts @@ -0,0 +1,43 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { Attributes, Span, SpanKind, Tracer } from '@opentelemetry/api'; +import { + AwsSdkInstrumentationConfig, + NormalizedRequest, + NormalizedResponse, +} from '@opentelemetry/instrumentation-aws-sdk'; +import { AWS_ATTRIBUTE_KEYS } from '../../../aws-attribute-keys'; +import { RequestMetadata, ServiceExtension } from '../../../third-party/otel/aws/services/ServiceExtension'; + +export class SecretsManagerServiceExtension implements ServiceExtension { + requestPreSpanHook(request: NormalizedRequest, _config: AwsSdkInstrumentationConfig): RequestMetadata { + const secretId = request.commandInput?.SecretId; + + const spanKind: SpanKind = SpanKind.CLIENT; + let spanName: string | undefined; + + const spanAttributes: Attributes = {}; + + if (typeof secretId === 'string' && secretId.startsWith('arn:aws:secretsmanager:')) { + spanAttributes[AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN] = secretId; + } + + const isIncoming = false; + + return { + isIncoming, + spanAttributes, + spanKind, + spanName, + }; + } + + responseHook(response: NormalizedResponse, span: Span, tracer: Tracer, config: AwsSdkInstrumentationConfig): void { + const secretArn = response.data.ARN; + + if (secretArn) { + span.setAttribute(AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN, secretArn); + } + } +} diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/step-functions.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/step-functions.ts new file mode 100644 index 0000000..4271cb0 --- /dev/null +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/step-functions.ts @@ -0,0 +1,36 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { Attributes, SpanKind } from '@opentelemetry/api'; +import { AwsSdkInstrumentationConfig, NormalizedRequest } from '@opentelemetry/instrumentation-aws-sdk'; +import { AWS_ATTRIBUTE_KEYS } from '../../../aws-attribute-keys'; +import { RequestMetadata, ServiceExtension } from '../../../third-party/otel/aws/services/ServiceExtension'; + +export class StepFunctionsServiceExtension implements ServiceExtension { + requestPreSpanHook(request: NormalizedRequest, _config: AwsSdkInstrumentationConfig): RequestMetadata { + const stateMachineArn = request.commandInput?.stateMachineArn; + const activityArn = request.commandInput?.activityArn; + + const spanKind: SpanKind = SpanKind.CLIENT; + let spanName: string | undefined; + + const spanAttributes: Attributes = {}; + + if (stateMachineArn) { + spanAttributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN] = stateMachineArn; + } + + if (activityArn) { + spanAttributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN] = activityArn; + } + + const isIncoming = false; + + return { + isIncoming, + spanAttributes, + spanKind, + spanName, + }; + } +} diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/aws-lambda.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/extended-instrumentations/aws-lambda.ts similarity index 100% rename from aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/aws-lambda.ts rename to aws-distro-opentelemetry-node-autoinstrumentation/src/patches/extended-instrumentations/aws-lambda.ts diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/extended-instrumentations/aws-sdk-instrumentation-extended.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/extended-instrumentations/aws-sdk-instrumentation-extended.ts new file mode 100644 index 0000000..b3044d6 --- /dev/null +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/extended-instrumentations/aws-sdk-instrumentation-extended.ts @@ -0,0 +1,42 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { AwsInstrumentation } from '@opentelemetry/instrumentation-aws-sdk'; +import { context as otelContext, defaultTextMapSetter } from '@opentelemetry/api'; +import { AWSXRayPropagator } from '@opentelemetry/propagator-aws-xray'; +import type { Command as AwsV3Command } from '@aws-sdk/types'; + +const awsXrayPropagator = new AWSXRayPropagator(); +const V3_CLIENT_CONFIG_KEY = Symbol('opentelemetry.instrumentation.aws-sdk.client.config'); +type V3PluginCommand = AwsV3Command & { + [V3_CLIENT_CONFIG_KEY]?: any; +}; + +// This class extends the upstream AwsInstrumentation to override its patching mechanism of the `send` method. +// The overriden method will additionally update the AWS SDK middleware stack to inject the `X-Amzn-Trace-Id` HTTP header. +// +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +export class AwsSdkInstrumentationExtended extends AwsInstrumentation { + // Override the upstream private _getV3SmithyClientSendPatch method to add middleware to inject X-Ray Trace Context into HTTP Headers + // https://github.com/open-telemetry/opentelemetry-js-contrib/blob/instrumentation-aws-sdk-v0.48.0/plugins/node/opentelemetry-instrumentation-aws-sdk/src/aws-sdk.ts#L373-L384 + override _getV3SmithyClientSendPatch(original: (...args: unknown[]) => Promise) { + return function send(this: any, command: V3PluginCommand, ...args: unknown[]): Promise { + this.middlewareStack?.add( + (next: any, context: any) => async (middlewareArgs: any) => { + awsXrayPropagator.inject(otelContext.active(), middlewareArgs.request.headers, defaultTextMapSetter); + const result = await next(middlewareArgs); + return result; + }, + { + step: 'build', + name: '_adotInjectXrayContextMiddleware', + override: true, + } + ); + + command[V3_CLIENT_CONFIG_KEY] = this.config; + return original.apply(this, [command, ...args]); + }; + } +} diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/instrumentation-patch.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/instrumentation-patch.ts index 7cc98bd..74879b9 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/instrumentation-patch.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/instrumentation-patch.ts @@ -25,7 +25,11 @@ import { } from './aws/services/bedrock'; import { KinesisServiceExtension } from './aws/services/kinesis'; import { S3ServiceExtension } from './aws/services/s3'; -import { AwsLambdaInstrumentationPatch } from './aws/services/aws-lambda'; +import { SecretsManagerServiceExtension } from './aws/services/secretsmanager'; +import { StepFunctionsServiceExtension } from './aws/services/step-functions'; +import { InstrumentationConfigMap } from '@opentelemetry/auto-instrumentations-node'; +import { AwsSdkInstrumentationExtended } from './extended-instrumentations/aws-sdk-instrumentation-extended'; +import { AwsLambdaInstrumentationPatch } from './extended-instrumentations/aws-lambda'; export const traceContextEnvironmentKey = '_X_AMZN_TRACE_ID'; const awsPropagator = new AWSXRayPropagator(); @@ -38,7 +42,10 @@ export const headerGetter: TextMapGetter = { }, }; -export function applyInstrumentationPatches(instrumentations: Instrumentation[]): void { +export function applyInstrumentationPatches( + instrumentations: Instrumentation[], + instrumentationConfigs?: InstrumentationConfigMap +): void { /* Apply patches to upstream instrumentation libraries. @@ -50,18 +57,28 @@ export function applyInstrumentationPatches(instrumentations: Instrumentation[]) */ instrumentations.forEach((instrumentation, index) => { if (instrumentation.instrumentationName === '@opentelemetry/instrumentation-aws-sdk') { + diag.debug('Overriding aws sdk instrumentation'); + instrumentations[index] = new AwsSdkInstrumentationExtended( + instrumentationConfigs ? instrumentationConfigs['@opentelemetry/instrumentation-aws-sdk'] : undefined + ); + // Access private property servicesExtensions of AwsInstrumentation // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - const services: Map | undefined = (instrumentation as any).servicesExtensions?.services; + const services: Map | undefined = (instrumentations[index] as any).servicesExtensions + ?.services; if (services) { services.set('S3', new S3ServiceExtension()); services.set('Kinesis', new KinesisServiceExtension()); + services.set('SecretsManager', new SecretsManagerServiceExtension()); + services.set('SFN', new StepFunctionsServiceExtension()); services.set('Bedrock', new BedrockServiceExtension()); services.set('BedrockAgent', new BedrockAgentServiceExtension()); services.set('BedrockAgentRuntime', new BedrockAgentRuntimeServiceExtension()); services.set('BedrockRuntime', new BedrockRuntimeServiceExtension()); patchSqsServiceExtension(services.get('SQS')); + patchSnsServiceExtension(services.get('SNS')); + patchLambdaServiceExtension(services.get('Lambda')); } } else if (instrumentation.instrumentationName === '@opentelemetry/instrumentation-aws-lambda') { diag.debug('Overriding aws lambda instrumentation'); @@ -143,3 +160,65 @@ function patchSqsServiceExtension(sqsServiceExtension: any): void { sqsServiceExtension.requestPreSpanHook = patchedRequestPreSpanHook; } } + +/* + * This patch extends the existing upstream extension for SNS. Extensions allow for custom logic for adding + * service-specific information to spans, such as attributes. Specifically, we are adding logic to add + * `aws.sns.topic.arn` attribute, to be used to generate RemoteTarget and achieve parity with the Java/Python instrumentation. + * + * + * @param snsServiceExtension SNS Service Extension obtained the service extension list from the AWS SDK OTel Instrumentation + */ +function patchSnsServiceExtension(snsServiceExtension: any): void { + if (snsServiceExtension) { + const requestPreSpanHook = snsServiceExtension.requestPreSpanHook; + snsServiceExtension._requestPreSpanHook = requestPreSpanHook; + + const patchedRequestPreSpanHook = ( + request: NormalizedRequest, + _config: AwsSdkInstrumentationConfig + ): RequestMetadata => { + const requestMetadata: RequestMetadata = snsServiceExtension._requestPreSpanHook(request, _config); + if (requestMetadata.spanAttributes) { + const topicArn = request.commandInput?.TopicArn; + if (topicArn) { + requestMetadata.spanAttributes[AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN] = topicArn; + } + } + return requestMetadata; + }; + + snsServiceExtension.requestPreSpanHook = patchedRequestPreSpanHook; + } +} + +/* + * This patch extends the existing upstream extension for Lambda. Extensions allow for custom logic for adding + * service-specific information to spans, such as attributes. Specifically, we are adding logic to add + * `aws.lambda.resource_mapping.id` attribute, to be used to generate RemoteTarget and achieve parity with the Java/Python instrumentation. + * + * + * @param lambdaServiceExtension Lambda Service Extension obtained the service extension list from the AWS SDK OTel Instrumentation + */ +function patchLambdaServiceExtension(lambdaServiceExtension: any): void { + if (lambdaServiceExtension) { + const requestPreSpanHook = lambdaServiceExtension.requestPreSpanHook; + lambdaServiceExtension._requestPreSpanHook = requestPreSpanHook; + + const patchedRequestPreSpanHook = ( + request: NormalizedRequest, + _config: AwsSdkInstrumentationConfig + ): RequestMetadata => { + const requestMetadata: RequestMetadata = lambdaServiceExtension._requestPreSpanHook(request, _config); + if (requestMetadata.spanAttributes) { + const resourceMappingId = request.commandInput?.UUID; + if (resourceMappingId) { + requestMetadata.spanAttributes[AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID] = resourceMappingId; + } + } + return requestMetadata; + }; + + lambdaServiceExtension.requestPreSpanHook = patchedRequestPreSpanHook; + } +} diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/register.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/register.ts index aed378d..751c0ff 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/src/register.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/register.ts @@ -25,14 +25,11 @@ diag.setLogger(new DiagConsoleLogger(), opentelemetry.core.getEnv().OTEL_LOG_LEV /* Sets up default environment variables and apply patches -Set default OTEL_EXPORTER_OTLP_PROTOCOL to be `http/protobuf`. This must be run before `configurator.configure()`, which will use this value to -create an OTel Metric Exporter that is used for the customized AWS Span Procesors. The default value of OTEL_EXPORTER_OTLP_PROTOCOL should be `http/protobuf`: +Set default OTEL_EXPORTER_OTLP_PROTOCOL to be `http/protobuf` to remain consistent with other ADOT languages. This must be run before +`configurator.configure()`, which will use this value to create an OTel Metric Exporter that is used for the customized AWS Span Procesors. +The default value of OTEL_EXPORTER_OTLP_PROTOCOL should be `http/protobuf`: https://github.com/open-telemetry/opentelemetry-js/blob/34003c9b7ef7e7e95e86986550d1c7fb6c1c56c6/packages/opentelemetry-core/src/utils/environment.ts#L233 -We are setting OTEL_EXPORTER_OTLP_PROTOCOL to HTTP to avoid any potential issues with gRPC. In the ADOT Python SDKs, gRPC did not not work out of the box for -the vended docker image, due to gRPC having a strict dependency on the Python version the artifact was built for (OTEL observed this: -https://github.com/open-telemetry/opentelemetry-operator/blob/461ba68e80e8ac6bf2603eb353547cd026119ed2/autoinstrumentation/python/requirements.txt#L2-L3) - Also sets default OTEL_PROPAGATORS to ensure good compatibility with X-Ray and Application Signals. This file may also be used to apply patches to upstream instrumentation - usually these are stopgap measures until we can contribute @@ -65,7 +62,7 @@ const instrumentationConfigs: InstrumentationConfigMap = { const instrumentations: Instrumentation[] = getNodeAutoInstrumentations(instrumentationConfigs); // Apply instrumentation patches -applyInstrumentationPatches(instrumentations); +applyInstrumentationPatches(instrumentations, instrumentationConfigs); const configurator: AwsOpentelemetryConfigurator = new AwsOpentelemetryConfigurator(instrumentations, useXraySampler); const configuration: Partial = configurator.configure(); diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/test/aws-metric-attribute-generator.test.ts b/aws-distro-opentelemetry-node-autoinstrumentation/test/aws-metric-attribute-generator.test.ts index e74eda3..0e0a7f2 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/test/aws-metric-attribute-generator.test.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/test/aws-metric-attribute-generator.test.ts @@ -761,6 +761,39 @@ describe('AwsMetricAttributeGeneratorTest', () => { validateRemoteResourceAttributes('AWS::Kinesis::Stream', 'AWS_KINESIS_STREAM_NAME'); mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_KINESIS_STREAM_NAME, undefined); + // Validate behaviour of AWS_SNS_TOPIC_ARN attribute then remove it. + mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN, 'arn:aws:sns:us-east-1:123456789012:testTopic'); + validateRemoteResourceAttributes('AWS::SNS::Topic', 'testTopic'); + mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN, undefined); + + // Validate behaviour of AWS_SECRETSMANAGER_SECRET_ARN attributes then remove it. + mockAttribute( + AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN, + 'arn:aws:secretsmanager:us-east-1:123456789123:secret:testSecret' + ); + validateRemoteResourceAttributes('AWS::SecretsManager::Secret', 'testSecret'); + mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN, undefined); + + // Validate behaviour of AWS_LAMBDA_RESOURCE_MAPPING_ID attribute then remove it. + mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID, 'aws_lambda_resource_mapping_id'); + validateRemoteResourceAttributes('AWS::Lambda::EventSourceMapping', 'aws_lambda_resource_mapping_id'); + mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID, undefined); + + // Validate behaviour of AWS_STEPFUNCTIONS_STATEMACHINE_ARN and AWS_STEPFUNCTIONS_ACTIVITY_ARN attributes then remove them. + mockAttribute( + AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN, + 'arn:aws:states:us-east-1:123456789123:stateMachine:testStateMachine' + ); + validateRemoteResourceAttributes('AWS::StepFunctions::StateMachine', 'testStateMachine'); + mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN, undefined); + + mockAttribute( + AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN, + 'arn:aws:states:us-east-1:123456789123:activity:testActivity' + ); + validateRemoteResourceAttributes('AWS::StepFunctions::Activity', 'testActivity'); + mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN, undefined); + // Validate behaviour of AWS_TABLE_NAMES attribute with one table name, then remove it. mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_DYNAMODB_TABLE_NAMES, ['aws_table_name']); validateRemoteResourceAttributes('AWS::DynamoDB::Table', 'aws_table_name'); @@ -1167,6 +1200,10 @@ describe('AwsMetricAttributeGeneratorTest', () => { testAwsSdkServiceNormalization('Kinesis', 'AWS::Kinesis'); testAwsSdkServiceNormalization('S3', 'AWS::S3'); testAwsSdkServiceNormalization('SQS', 'AWS::SQS'); + testAwsSdkServiceNormalization('SNS', 'AWS::SNS'); + testAwsSdkServiceNormalization('Lambda', 'AWS::Lambda'); + testAwsSdkServiceNormalization('SecretsManager', 'AWS::SecretsManager'); + testAwsSdkServiceNormalization('SFN', 'AWS::StepFunctions'); testAwsSdkServiceNormalization('Bedrock', 'AWS::Bedrock'); testAwsSdkServiceNormalization('BedrockAgent', 'AWS::Bedrock'); testAwsSdkServiceNormalization('BedrockAgentRuntime', 'AWS::Bedrock'); diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/bedrock.test.ts b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/bedrock.test.ts index 23edc00..1759774 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/bedrock.test.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/bedrock.test.ts @@ -134,7 +134,7 @@ describe('BedrockAgent', () => { expect(getDataSourceSpans.length).toBe(1); const getDataSourceSpan = getDataSourceSpans[0]; expect(getDataSourceSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); - expect(getDataSourceSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); + expect(getDataSourceSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBe(dummyKnowledgeBaseId); expect(getDataSourceSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBe(dummyDataSourceId); expect(getDataSourceSpan.kind).toBe(SpanKind.CLIENT); }); @@ -236,9 +236,11 @@ describe('Bedrock', () => { describe('GetGuardrail', () => { it('adds guardrailId to span', async () => { const dummyGuardrailIdentifier: string = 'ABCDEFGH'; + const guardrailArn: string = 'arn:aws:bedrock:us-east-1:123456789012:guardrail/abc123'; nock(`https://bedrock.${region}.amazonaws.com`).get(`/guardrails/${dummyGuardrailIdentifier}`).reply(200, { guardrailId: dummyGuardrailIdentifier, + guardrailArn: guardrailArn, }); await bedrock @@ -258,6 +260,7 @@ describe('Bedrock', () => { expect(getGuardrailSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); expect(getGuardrailSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); expect(getGuardrailSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID]).toBe(dummyGuardrailIdentifier); + expect(getGuardrailSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ARN]).toBe(guardrailArn); expect(getGuardrailSpan.kind).toBe(SpanKind.CLIENT); }); }); @@ -276,19 +279,102 @@ describe('BedrockRuntime', () => { }); describe('InvokeModel', () => { - it('adds modelId to span', async () => { - const dummyModelId: string = 'ABCDEFGH'; - const dummyBody: string = 'HGFEDCBA'; + it('Add AI21 Jamba model attributes to span', async () => { + const modelId: string = 'ai21.jamba-1-5-large-v1:0'; + const prompt: string = 'Describe the purpose of a compiler in one line.'; + const nativeRequest: any = { + messages: [ + { + role: 'user', + content: prompt, + }, + ], + top_p: 0.8, + temperature: 0.6, + max_tokens: 512, + }; + const mockRequestBody: string = JSON.stringify(nativeRequest); + const mockResponseBody: any = { + stop_reason: 'end_turn', + usage: { + prompt_tokens: 21, + completion_tokens: 24, + }, + choices: [ + { + finish_reason: 'stop', + }, + ], + request: { + commandInput: { + modelId: modelId, + }, + }, + }; + + nock(`https://bedrock-runtime.${region}.amazonaws.com`) + .post(`/model/${encodeURIComponent(modelId)}/invoke`) + .reply(200, mockResponseBody); + + await bedrock + .invokeModel({ + modelId: modelId, + body: mockRequestBody, + }) + .catch((err: any) => { + console.log('error', err); + }); - nock(`https://bedrock-runtime.${region}.amazonaws.com`).post(`/model/${dummyModelId}/invoke`).reply(200, { - modelId: dummyModelId, - body: dummyBody, + const testSpans: ReadableSpan[] = getTestSpans(); + const invokeModelSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'BedrockRuntime.InvokeModel'; }); + expect(invokeModelSpans.length).toBe(1); + const invokeModelSpan = invokeModelSpans[0]; + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws.bedrock'); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]).toBe(modelId); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS]).toBe(512); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE]).toBe(0.6); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P]).toBe(0.8); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS]).toBe(21); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS]).toBe(24); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS]).toEqual(['stop']); + expect(invokeModelSpan.kind).toBe(SpanKind.CLIENT); + }); + it('Add Amazon Titan model attributes to span', async () => { + const modelId: string = 'amazon.titan-text-express-v1'; + const prompt: string = 'Complete this text. It was the best of times it was the worst...'; + const nativeRequest: any = { + inputText: prompt, + textGenerationConfig: { + maxTokenCount: 4096, + stopSequences: [], + temperature: 0, + topP: 1, + }, + }; + const mockRequestBody: string = JSON.stringify(nativeRequest); + const mockResponseBody: any = { + inputTextTokenCount: 15, + results: [ + { + tokenCount: 13, + completionReason: 'CONTENT_FILTERED', + }, + ], + }; + + nock(`https://bedrock-runtime.${region}.amazonaws.com`) + .post(`/model/${modelId}/invoke`) + .reply(200, mockResponseBody); await bedrock .invokeModel({ - modelId: dummyModelId, - body: dummyBody, + modelId: modelId, + body: mockRequestBody, }) .catch((err: any) => {}); @@ -301,7 +387,360 @@ describe('BedrockRuntime', () => { expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); - expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]).toBe(dummyModelId); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws.bedrock'); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]).toBe(modelId); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS]).toBe(4096); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE]).toBe(0); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P]).toBe(1); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS]).toBe(15); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS]).toBe(13); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS]).toEqual([ + 'CONTENT_FILTERED', + ]); + expect(invokeModelSpan.kind).toBe(SpanKind.CLIENT); + }); + + it('Add Amazon Nova model attributes to span', async () => { + const modelId: string = 'amazon.nova-pro-v1:0'; + const prompt: string = 'Campfire story'; + const mockRequestBody: string = JSON.stringify({ + inputText: prompt, + inferenceConfig: { + max_new_tokens: 500, + temperature: 0.9, + top_p: 0.7, + }, + }); + const mockResponseBody: any = { + output: { message: { content: [{ text: '' }], role: 'assistant' } }, + stopReason: 'max_tokens', + usage: { inputTokens: 432, outputTokens: 681 }, + + request: { + commandInput: { + modelId: modelId, + }, + }, + }; + + nock(`https://bedrock-runtime.${region}.amazonaws.com`) + .post(`/model/${encodeURIComponent(modelId)}/invoke`) + .reply(200, mockResponseBody); + + await bedrock + .invokeModel({ + modelId: modelId, + body: mockRequestBody, + }) + .catch((err: any) => {}); + + const testSpans: ReadableSpan[] = getTestSpans(); + const invokeModelSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'BedrockRuntime.InvokeModel'; + }); + expect(invokeModelSpans.length).toBe(1); + const invokeModelSpan = invokeModelSpans[0]; + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws.bedrock'); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]).toBe(modelId); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS]).toBe(500); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE]).toBe(0.9); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P]).toBe(0.7); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS]).toBe(432); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS]).toBe(681); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS]).toEqual(['max_tokens']); + expect(invokeModelSpan.kind).toBe(SpanKind.CLIENT); + }); + + it('Add Anthropic Claude model attributes to span', async () => { + const modelId: string = 'anthropic.claude-3-5-sonnet-20240620-v1:0'; + const prompt: string = 'Complete this text. It was the best of times it was the worst...'; + const nativeRequest: any = { + anthropic_version: 'bedrock-2023-05-31', + max_tokens: 1000, + temperature: 1.0, + top_p: 1, + messages: [ + { + role: 'user', + content: [{ type: 'text', text: prompt }], + }, + ], + }; + const mockRequestBody: string = JSON.stringify(nativeRequest); + const mockResponseBody: any = { + stop_reason: 'end_turn', + usage: { + input_tokens: 15, + output_tokens: 13, + }, + request: { + commandInput: { + modelId: modelId, + }, + }, + }; + + nock(`https://bedrock-runtime.${region}.amazonaws.com`) + .post(`/model/${encodeURIComponent(modelId)}/invoke`) + .reply(200, mockResponseBody); + + await bedrock + .invokeModel({ + modelId: modelId, + body: mockRequestBody, + }) + .catch((err: any) => { + console.log('error', err); + }); + + const testSpans: ReadableSpan[] = getTestSpans(); + const invokeModelSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'BedrockRuntime.InvokeModel'; + }); + expect(invokeModelSpans.length).toBe(1); + const invokeModelSpan = invokeModelSpans[0]; + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws.bedrock'); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]).toBe(modelId); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS]).toBe(1000); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE]).toBe(1.0); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P]).toBe(1); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS]).toBe(15); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS]).toBe(13); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS]).toEqual(['end_turn']); + expect(invokeModelSpan.kind).toBe(SpanKind.CLIENT); + }); + + it('Add Cohere Command model attributes to span', async () => { + const modelId: string = 'cohere.command-light-text-v14'; + const prompt: string = "Describe the purpose of a 'hello world' program in one line"; + const nativeRequest: any = { + prompt: prompt, + max_tokens: 512, + temperature: 0.5, + p: 0.65, + }; + const mockRequestBody: string = JSON.stringify(nativeRequest); + const mockResponseBody: any = { + generations: [ + { + finish_reason: 'COMPLETE', + text: 'test-generation-text', + }, + ], + prompt: prompt, + request: { + commandInput: { + modelId: modelId, + }, + }, + }; + + nock(`https://bedrock-runtime.${region}.amazonaws.com`) + .post(`/model/${encodeURIComponent(modelId)}/invoke`) + .reply(200, mockResponseBody); + + await bedrock + .invokeModel({ + modelId: modelId, + body: mockRequestBody, + }) + .catch((err: any) => { + console.log('error', err); + }); + + const testSpans: ReadableSpan[] = getTestSpans(); + const invokeModelSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'BedrockRuntime.InvokeModel'; + }); + expect(invokeModelSpans.length).toBe(1); + const invokeModelSpan = invokeModelSpans[0]; + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws.bedrock'); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]).toBe(modelId); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS]).toBe(512); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE]).toBe(0.5); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P]).toBe(0.65); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS]).toBe(10); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS]).toBe(4); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS]).toEqual(['COMPLETE']); + expect(invokeModelSpan.kind).toBe(SpanKind.CLIENT); + }); + + it('Add Cohere Command R model attributes to span', async () => { + const modelId: string = 'cohere.command-r-v1:0"'; + const prompt: string = "Describe the purpose of a 'hello world' program in one line"; + const nativeRequest: any = { + message: prompt, + max_tokens: 512, + temperature: 0.5, + p: 0.65, + }; + const mockRequestBody: string = JSON.stringify(nativeRequest); + const mockResponseBody: any = { + finish_reason: 'COMPLETE', + text: 'test-generation-text', + prompt: prompt, + request: { + commandInput: { + modelId: modelId, + }, + }, + }; + + nock(`https://bedrock-runtime.${region}.amazonaws.com`) + .post(`/model/${encodeURIComponent(modelId)}/invoke`) + .reply(200, mockResponseBody); + + await bedrock + .invokeModel({ + modelId: modelId, + body: mockRequestBody, + }) + .catch((err: any) => { + console.log('error', err); + }); + + const testSpans: ReadableSpan[] = getTestSpans(); + const invokeModelSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'BedrockRuntime.InvokeModel'; + }); + expect(invokeModelSpans.length).toBe(1); + const invokeModelSpan = invokeModelSpans[0]; + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws.bedrock'); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]).toBe(modelId); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS]).toBe(512); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE]).toBe(0.5); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P]).toBe(0.65); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS]).toBe(10); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS]).toBe(4); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS]).toEqual(['COMPLETE']); + expect(invokeModelSpan.kind).toBe(SpanKind.CLIENT); + }); + + it('Add Meta Llama model attributes to span', async () => { + const modelId: string = 'meta.llama2-13b-chat-v1'; + const prompt: string = 'Describe the purpose of an interpreter program in one line.'; + const nativeRequest: any = { + prompt, + max_gen_len: 512, + temperature: 0.5, + top_p: 0.9, + }; + const mockRequestBody: string = JSON.stringify(nativeRequest); + const mockResponseBody: any = { + prompt_token_count: 31, + generation_token_count: 49, + stop_reason: 'stop', + request: { + commandInput: { + modelId: modelId, + }, + }, + }; + + nock(`https://bedrock-runtime.${region}.amazonaws.com`) + .post(`/model/${encodeURIComponent(modelId)}/invoke`) + .reply(200, mockResponseBody); + + await bedrock + .invokeModel({ + modelId: modelId, + body: mockRequestBody, + }) + .catch((err: any) => { + console.log('error', err); + }); + + const testSpans: ReadableSpan[] = getTestSpans(); + const invokeModelSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'BedrockRuntime.InvokeModel'; + }); + expect(invokeModelSpans.length).toBe(1); + const invokeModelSpan = invokeModelSpans[0]; + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws.bedrock'); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]).toBe(modelId); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS]).toBe(512); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE]).toBe(0.5); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P]).toBe(0.9); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS]).toBe(31); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS]).toBe(49); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS]).toEqual(['stop']); + expect(invokeModelSpan.kind).toBe(SpanKind.CLIENT); + }); + + it('Add Mistral AI model attributes to span', async () => { + const modelId: string = 'mistral.mistral-7b-instruct-v0:2'; + const prompt: string = ` + [INST] + In Bash, how do I list all text files in the current directory + (excluding subdirectories) that have been modified in the last month? + [/INST] + `; + const nativeRequest: any = { + prompt: prompt, + max_tokens: 4096, + temperature: 0.75, + top_p: 1.0, + }; + const mockRequestBody: string = JSON.stringify(nativeRequest); + const mockResponseBody: any = { + outputs: [ + { + text: 'test-output-text', + stop_reason: 'stop', + }, + ], + request: { + commandInput: { + modelId: modelId, + }, + }, + }; + + nock(`https://bedrock-runtime.${region}.amazonaws.com`) + .post(`/model/${encodeURIComponent(modelId)}/invoke`) + .reply(200, mockResponseBody); + + await bedrock + .invokeModel({ + modelId: modelId, + body: mockRequestBody, + }) + .catch((err: any) => { + console.log('error', err); + }); + + const testSpans: ReadableSpan[] = getTestSpans(); + const invokeModelSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'BedrockRuntime.InvokeModel'; + }); + expect(invokeModelSpans.length).toBe(1); + const invokeModelSpan = invokeModelSpans[0]; + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws.bedrock'); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]).toBe(modelId); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS]).toBe(4096); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE]).toBe(0.75); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P]).toBe(1.0); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS]).toBe(31); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS]).toBe(3); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS]).toEqual(['stop']); expect(invokeModelSpan.kind).toBe(SpanKind.CLIENT); }); }); diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/secretsmanager.test.ts b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/secretsmanager.test.ts new file mode 100644 index 0000000..7e84777 --- /dev/null +++ b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/secretsmanager.test.ts @@ -0,0 +1,100 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { getTestSpans, registerInstrumentationTesting } from '@opentelemetry/contrib-test-utils'; +import { AwsInstrumentation } from '@opentelemetry/instrumentation-aws-sdk'; +import { applyInstrumentationPatches } from './../../../../src/patches/instrumentation-patch'; + +const instrumentations: AwsInstrumentation[] = [new AwsInstrumentation()]; +applyInstrumentationPatches(instrumentations); +registerInstrumentationTesting(instrumentations[0]); + +import { SecretsManager } from '@aws-sdk/client-secrets-manager'; +import * as nock from 'nock'; + +import { SpanKind } from '@opentelemetry/api'; +import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import { expect } from 'expect'; +import { AWS_ATTRIBUTE_KEYS } from '../../../../src/aws-attribute-keys'; + +const region = 'us-east-1'; + +describe('SecretsManager', () => { + let secretsManager: SecretsManager; + beforeEach(() => { + secretsManager = new SecretsManager({ + region: region, + credentials: { + accessKeyId: 'abcde', + secretAccessKey: 'abcde', + }, + }); + }); + + describe('DescribeSecret', () => { + const testParams = [ + 'testId', + 'badarn:aws:secretsmanager:us-weast-1:123456789123:secret:testId123456', + 'arn:aws:secretsmanager:us-east-1:123456789123:secret:testId123456', + ]; + + testParams.forEach(secretId => { + it('should generate secret arn attribute only if secretId is an valid ARN', async () => { + nock(`https://secretsmanager.${region}.amazonaws.com/`).post('/').reply(200, 'null'); + + await secretsManager + .describeSecret({ + SecretId: secretId, + }) + .catch((err: any) => {}); + + const testSpans: ReadableSpan[] = getTestSpans(); + const getDescribeSecretSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'SecretsManager.DescribeSecret'; + }); + + expect(getDescribeSecretSpans.length).toBe(1); + const describeSecretSpan = getDescribeSecretSpans[0]; + + if (secretId.startsWith('arn:aws:secretsmanager:')) { + expect(describeSecretSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN]).toBe(secretId); + } else { + expect(describeSecretSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN]).toBeUndefined(); + } + + expect(describeSecretSpan.kind).toBe(SpanKind.CLIENT); + }); + }); + }); + + describe('GetSecretValue', () => { + it('secret arn attribute should be populated from the response', async () => { + const secretIdArn = 'arn:aws:secretsmanager:us-east-1:123456789123:secret:testId123456'; + + nock(`https://secretsmanager.${region}.amazonaws.com/`).post('/').reply(200, { + ARN: secretIdArn, + Name: 'testId', + }); + + await secretsManager + .getSecretValue({ + SecretId: 'testSecret', + }) + .catch((err: any) => { + console.log(err); + }); + + const testSpans: ReadableSpan[] = getTestSpans(); + const getSecretValueSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'SecretsManager.GetSecretValue'; + }); + + expect(getSecretValueSpans.length).toBe(1); + + const secretValueSpan = getSecretValueSpans[0]; + + expect(secretValueSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN]).toBe(secretIdArn); + expect(secretValueSpan.kind).toBe(SpanKind.CLIENT); + }); + }); +}); diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/step-functions.test.ts b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/step-functions.test.ts new file mode 100644 index 0000000..8d9a5a5 --- /dev/null +++ b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/step-functions.test.ts @@ -0,0 +1,93 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { getTestSpans, registerInstrumentationTesting } from '@opentelemetry/contrib-test-utils'; +import { AwsInstrumentation } from '@opentelemetry/instrumentation-aws-sdk'; +import { applyInstrumentationPatches } from './../../../../src/patches/instrumentation-patch'; + +const instrumentations: AwsInstrumentation[] = [new AwsInstrumentation()]; +applyInstrumentationPatches(instrumentations); +registerInstrumentationTesting(instrumentations[0]); + +import { SFN } from '@aws-sdk/client-sfn'; +import * as nock from 'nock'; + +import { SpanKind } from '@opentelemetry/api'; +import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import { expect } from 'expect'; +import { AWS_ATTRIBUTE_KEYS } from '../../../../src/aws-attribute-keys'; + +const region = 'us-east-1'; + +describe('SFN', () => { + let sfn: SFN; + beforeEach(() => { + sfn = new SFN({ + region: region, + credentials: { + accessKeyId: 'abcde', + secretAccessKey: 'abcde', + }, + }); + }); + + describe('DescribeStateMachine', () => { + it('span has stateMachineArn in its attributes', async () => { + const stateMachineArn: string = 'arn:aws:states:us-east-1:123456789123:stateMachine:testStateMachine'; + + nock(`https://states.${region}.amazonaws.com/`).post('/').reply(200, 'null'); + + await sfn + .describeStateMachine({ + stateMachineArn: stateMachineArn, + }) + .catch((err: any) => { + console.log(err); + }); + + const testSpans: ReadableSpan[] = getTestSpans(); + const getStateMachineAttributeSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'SFN.DescribeStateMachine'; + }); + + expect(getStateMachineAttributeSpans.length).toBe(1); + + const stateMachineAttributeSpan = getStateMachineAttributeSpans[0]; + + expect(AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN in stateMachineAttributeSpan.attributes).toBe(true); + expect(stateMachineAttributeSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN]).toBe( + stateMachineArn + ); + expect(stateMachineAttributeSpan.kind).toBe(SpanKind.CLIENT); + }); + }); + + describe('DescribeActivity', () => { + it('span has activityArn in its attributes', async () => { + const activityArn: string = 'arn:aws:states:us-east-1:123456789123:activity:testActivity'; + + nock(`https://states.${region}.amazonaws.com/`).post('/').reply(200, 'null'); + + await sfn + .describeActivity({ + activityArn: activityArn, + }) + .catch((err: any) => { + console.log(err); + }); + + const testSpans: ReadableSpan[] = getTestSpans(); + const getActivityAttributeSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'SFN.DescribeActivity'; + }); + + expect(getActivityAttributeSpans.length).toBe(1); + + const activityAttributeSpan = getActivityAttributeSpans[0]; + + expect(AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN in activityAttributeSpan.attributes).toBe(true); + expect(activityAttributeSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN]).toBe(activityArn); + expect(activityAttributeSpan.kind).toBe(SpanKind.CLIENT); + }); + }); +}); diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/aws-lambda.test.ts b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/extended-instrumentations/aws-lambda.test.ts similarity index 98% rename from aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/aws-lambda.test.ts rename to aws-distro-opentelemetry-node-autoinstrumentation/test/patches/extended-instrumentations/aws-lambda.test.ts index d68e1e6..0580769 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/aws-lambda.test.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/extended-instrumentations/aws-lambda.test.ts @@ -6,7 +6,7 @@ import * as path from 'path'; import * as fs from 'fs'; import { diag } from '@opentelemetry/api'; import { InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation'; -import { AwsLambdaInstrumentationPatch } from '../../../../src/patches/aws/services/aws-lambda'; +import { AwsLambdaInstrumentationPatch } from '../../../src/patches/extended-instrumentations/aws-lambda'; describe('AwsLambdaInstrumentationPatch', () => { let instrumentation: AwsLambdaInstrumentationPatch; diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/extended-instrumentations/aws-sdk-instrumentation-extended.test.ts b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/extended-instrumentations/aws-sdk-instrumentation-extended.test.ts new file mode 100644 index 0000000..9751d21 --- /dev/null +++ b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/extended-instrumentations/aws-sdk-instrumentation-extended.test.ts @@ -0,0 +1,48 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import * as sinon from 'sinon'; +import { AwsSdkInstrumentationExtended } from '../../../src/patches/extended-instrumentations/aws-sdk-instrumentation-extended'; +import expect from 'expect'; +import { AWSXRayPropagator } from '@opentelemetry/propagator-aws-xray'; +import { Context, TextMapSetter } from '@opentelemetry/api'; + +describe('AwsSdkInstrumentationExtended', () => { + let instrumentation: AwsSdkInstrumentationExtended; + + beforeEach(() => { + instrumentation = new AwsSdkInstrumentationExtended({}); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('overridden _getV3SmithyClientSendPatch updates MiddlewareStack', async () => { + const mockedMiddlewareStackInternal: any = []; + const mockedMiddlewareStack = { + add: (arg1: any, arg2: any) => mockedMiddlewareStackInternal.push([arg1, arg2]), + }; + const send = instrumentation + ._getV3SmithyClientSendPatch((...args: unknown[]) => Promise.resolve()) + .bind({ middlewareStack: mockedMiddlewareStack }); + sinon + .stub(AWSXRayPropagator.prototype, 'inject') + .callsFake((context: Context, carrier: unknown, setter: TextMapSetter) => { + (carrier as any)['isCarrierModified'] = 'carrierIsModified'; + }); + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + await send({}, null); + + const middlewareArgs: any = { + request: { + headers: {}, + }, + }; + await mockedMiddlewareStackInternal[0][0]((arg: any) => Promise.resolve(), null)(middlewareArgs); + + expect(middlewareArgs.request.headers['isCarrierModified']).toEqual('carrierIsModified'); + expect(mockedMiddlewareStackInternal[0][1].name).toEqual('_adotInjectXrayContextMiddleware'); + }); +}); diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/instrumentation-patch.test.ts b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/instrumentation-patch.test.ts index 385c7d7..6a731f2 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/instrumentation-patch.test.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/instrumentation-patch.test.ts @@ -23,16 +23,27 @@ import * as sinon from 'sinon'; import { AWSXRAY_TRACE_ID_HEADER, AWSXRayPropagator } from '@opentelemetry/propagator-aws-xray'; import { Context } from 'aws-lambda'; import { SinonStub } from 'sinon'; +import { S3 } from '@aws-sdk/client-s3'; +import nock = require('nock'); +import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import { getTestSpans, registerInstrumentationTesting } from '@opentelemetry/contrib-test-utils'; +import { AwsSdkInstrumentationExtended } from '../../src/patches/extended-instrumentations/aws-sdk-instrumentation-extended'; const _STREAM_NAME: string = 'streamName'; const _BUCKET_NAME: string = 'bucketName'; const _QUEUE_NAME: string = 'queueName'; +const _ACTIVITY_ARN: string = 'arn:aws:states:us-east-1:123456789123:activity:testActivity'; +const _STATE_MACHINE_ARN: string = 'arn:aws:states:us-east-1:123456789123:stateMachine:testStateMachine'; +const _SECRETS_ARN: string = 'arn:aws:secretsmanager:us-east-1:123456789123:secret:testId123456'; +const _UUID: string = 'random-uuid'; +const _TOPIC_ARN: string = 'arn:aws:sns:us-east-1:123456789012:mystack-mytopic-NZJ5JSMVGFIE'; const _QUEUE_URL: string = 'https://sqs.us-east-1.amazonaws.com/123412341234/queueName'; const _BEDROCK_AGENT_ID: string = 'agentId'; const _BEDROCK_DATASOURCE_ID: string = 'DataSourceId'; const _BEDROCK_GUARDRAIL_ID: string = 'GuardrailId'; +const _BEDROCK_GUARDRAIL_ARN: string = 'arn:aws:bedrock:us-east-1:123456789012:guardrail/abc123'; const _BEDROCK_KNOWLEDGEBASE_ID: string = 'KnowledgeBaseId'; -const _GEN_AI_SYSTEM: string = 'aws_bedrock'; +const _GEN_AI_SYSTEM: string = 'aws.bedrock'; const _GEN_AI_REQUEST_MODEL: string = 'genAiReuqestModelId'; const mockHeaders = { @@ -45,6 +56,10 @@ const UNPATCHED_INSTRUMENTATIONS: Instrumentation[] = getNodeAutoInstrumentation const PATCHED_INSTRUMENTATIONS: Instrumentation[] = getNodeAutoInstrumentations(); applyInstrumentationPatches(PATCHED_INSTRUMENTATIONS); +const extendedAwsSdkInstrumentation: AwsInstrumentation = new AwsInstrumentation(); +applyInstrumentationPatches([extendedAwsSdkInstrumentation]); +registerInstrumentationTesting(extendedAwsSdkInstrumentation); + describe('InstrumentationPatchTest', () => { it('SanityTestUnpatchedAwsSdkInstrumentation', () => { const awsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(UNPATCHED_INSTRUMENTATIONS); @@ -53,11 +68,18 @@ describe('InstrumentationPatchTest', () => { // Not from patching expect(services.has('SQS')).toBeTruthy(); expect(services.has('SNS')).toBeTruthy(); - expect(services.has('DynamoDB')).toBeTruthy(); expect(services.has('Lambda')).toBeTruthy(); + + expect(services.has('DynamoDB')).toBeTruthy(); // From patching but shouldn't be applied + expect(services.get('SecretsManager')).toBeFalsy(); + expect(services.get('SFN')).toBeFalsy(); expect(services.has('S3')).toBeFalsy(); expect(services.has('Kinesis')).toBeFalsy(); + expect(services.get('SNS')._requestPreSpanHook).toBeFalsy(); + expect(services.get('SNS').requestPreSpanHook).toBeTruthy(); + expect(services.get('Lambda')._requestPreSpanHook).toBeFalsy(); + expect(services.get('Lambda').requestPreSpanHook).toBeTruthy(); expect(services.get('SQS')._requestPreSpanHook).toBeFalsy(); expect(services.get('SQS').requestPreSpanHook).toBeTruthy(); expect(services.has('Bedrock')).toBeFalsy(); @@ -78,9 +100,16 @@ describe('InstrumentationPatchTest', () => { expect(services.has('SNS')).toBeTruthy(); expect(services.has('DynamoDB')).toBeTruthy(); expect(services.has('Lambda')).toBeTruthy(); + // From patching + expect(services.has('SecretsManager')).toBeTruthy(); + expect(services.has('SFN')).toBeTruthy(); expect(services.has('S3')).toBeTruthy(); expect(services.has('Kinesis')).toBeTruthy(); + expect(services.get('SNS')._requestPreSpanHook).toBeTruthy(); + expect(services.get('SNS').requestPreSpanHook).toBeTruthy(); + expect(services.get('Lambda')._requestPreSpanHook).toBeTruthy(); + expect(services.get('Lambda').requestPreSpanHook).toBeTruthy(); expect(services.get('SQS')._requestPreSpanHook).toBeTruthy(); expect(services.get('SQS').requestPreSpanHook).toBeTruthy(); expect(services.has('Bedrock')).toBeTruthy(); @@ -89,6 +118,9 @@ describe('InstrumentationPatchTest', () => { expect(services.get('BedrockRuntime')).toBeTruthy(); // Sanity check expect(services.has('InvalidService')).toBeFalsy(); + + // Check that the original AWS SDK Instrumentation is replaced with the extended version + expect(awsSdkInstrumentation).toBeInstanceOf(AwsSdkInstrumentationExtended); }); it('S3 without patching', () => { @@ -117,6 +149,36 @@ describe('InstrumentationPatchTest', () => { expect(sqsAttributes[AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_NAME]).toBeUndefined(); }); + it('SNS without patching', () => { + const unpatchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(UNPATCHED_INSTRUMENTATIONS); + const services: Map = extractServicesFromAwsSdkInstrumentation(unpatchedAwsSdkInstrumentation); + expect(() => doExtractSNSAttributes(services)).not.toThrow(); + + const snsAttributes = doExtractSNSAttributes(services); + expect(snsAttributes[AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN]).toBeUndefined(); + }); + + it('Lambda without patching', () => { + const unpatchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(UNPATCHED_INSTRUMENTATIONS); + const services: Map = extractServicesFromAwsSdkInstrumentation(unpatchedAwsSdkInstrumentation); + expect(() => doExtractLambdaAttributes(services)).not.toThrow(); + + const lambdaAttributes: Attributes = doExtractLambdaAttributes(services); + expect(lambdaAttributes[AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID]).toBeUndefined(); + }); + + it('SFN without patching', () => { + const unpatchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(UNPATCHED_INSTRUMENTATIONS); + const services: Map = extractServicesFromAwsSdkInstrumentation(unpatchedAwsSdkInstrumentation); + expect(() => doExtractSFNAttributes(services)).toThrow(); + }); + + it('SecretsManager without patching', () => { + const unpatchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(UNPATCHED_INSTRUMENTATIONS); + const services: Map = extractServicesFromAwsSdkInstrumentation(unpatchedAwsSdkInstrumentation); + expect(() => doExtractSecretsManagerAttributes(services)).toThrow(); + }); + it('Bedrock without patching', () => { const unpatchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(UNPATCHED_INSTRUMENTATIONS); const services: Map = extractServicesFromAwsSdkInstrumentation(unpatchedAwsSdkInstrumentation); @@ -137,6 +199,14 @@ describe('InstrumentationPatchTest', () => { expect(kinesisAttributes[AWS_ATTRIBUTE_KEYS.AWS_KINESIS_STREAM_NAME]).toEqual(_STREAM_NAME); }); + it('SNS with patching', () => { + const patchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(PATCHED_INSTRUMENTATIONS); + const services: Map = extractServicesFromAwsSdkInstrumentation(patchedAwsSdkInstrumentation); + + const snsAttributes = doExtractSNSAttributes(services); + expect(snsAttributes[AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN]).toBe(_TOPIC_ARN); + }); + it('SQS with patching', () => { const patchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(PATCHED_INSTRUMENTATIONS); const services: Map = extractServicesFromAwsSdkInstrumentation(patchedAwsSdkInstrumentation); @@ -153,6 +223,33 @@ describe('InstrumentationPatchTest', () => { expect(sqsAttributes[AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_NAME]).toEqual(_QUEUE_NAME); }); + it('Lambda with patching', () => { + const patchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(PATCHED_INSTRUMENTATIONS); + const services: Map = extractServicesFromAwsSdkInstrumentation(patchedAwsSdkInstrumentation); + const requestLambdaAttributes: Attributes = doExtractLambdaAttributes(services); + expect(requestLambdaAttributes[AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID]).toEqual(_UUID); + }); + + it('SFN with patching', () => { + const patchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(PATCHED_INSTRUMENTATIONS); + const services: Map = extractServicesFromAwsSdkInstrumentation(patchedAwsSdkInstrumentation); + const requestSFNAttributes: Attributes = doExtractSFNAttributes(services); + expect(requestSFNAttributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN]).toEqual(_STATE_MACHINE_ARN); + expect(requestSFNAttributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN]).toEqual(_ACTIVITY_ARN); + }); + + it('SecretsManager with patching', () => { + const patchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(PATCHED_INSTRUMENTATIONS); + const services: Map = extractServicesFromAwsSdkInstrumentation(patchedAwsSdkInstrumentation); + const requestSecretsManagerAttributes: Attributes = doExtractSecretsManagerAttributes(services); + + expect(requestSecretsManagerAttributes[AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN]).toBe(_SECRETS_ARN); + + const responseHookSecretsManagerAttributes = doResponseHookSecretsManager(services); + + expect(responseHookSecretsManagerAttributes[AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN]).toBe(_SECRETS_ARN); + }); + it('Bedrock with patching', () => { const patchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(PATCHED_INSTRUMENTATIONS); const services: Map = extractServicesFromAwsSdkInstrumentation(patchedAwsSdkInstrumentation); @@ -160,8 +257,11 @@ describe('InstrumentationPatchTest', () => { // Expect no-op from attribute extraction in Bedrock expect(Object.entries(bedrockAttributes).length).toEqual(0); const bedrockAttributesAfterResponse: Attributes = doResponseHookBedrock(services, 'Bedrock'); - expect(Object.entries(bedrockAttributesAfterResponse).length).toBe(1); + expect(Object.entries(bedrockAttributesAfterResponse).length).toBe(2); expect(bedrockAttributesAfterResponse[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID]).toEqual(_BEDROCK_GUARDRAIL_ID); + expect(bedrockAttributesAfterResponse[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ARN]).toEqual( + _BEDROCK_GUARDRAIL_ARN + ); }); it('Bedrock Agent with patching', () => { @@ -196,18 +296,25 @@ describe('InstrumentationPatchTest', () => { ListDataSources: { 'aws.bedrock.knowledge_base.id': _BEDROCK_KNOWLEDGEBASE_ID }, UpdateAgentKnowledgeBase: { 'aws.bedrock.knowledge_base.id': _BEDROCK_KNOWLEDGEBASE_ID }, DeleteDataSource: { 'aws.bedrock.data_source.id': _BEDROCK_DATASOURCE_ID }, - GetDataSource: { 'aws.bedrock.data_source.id': _BEDROCK_DATASOURCE_ID }, + GetDataSource: { + 'aws.bedrock.data_source.id': _BEDROCK_DATASOURCE_ID, + 'aws.bedrock.knowledge_base.id': _BEDROCK_KNOWLEDGEBASE_ID, + }, UpdateDataSource: { 'aws.bedrock.data_source.id': _BEDROCK_DATASOURCE_ID }, }; for (const [operation, attribute_tuple] of Object.entries(operation_to_expected_attribute)) { const bedrockAttributes: Attributes = doExtractBedrockAttributes(services, 'BedrockAgent', operation); - const [attribute_key, attribute_value] = Object.entries(attribute_tuple)[0]; - expect(Object.entries(bedrockAttributes).length).toBe(1); - expect(bedrockAttributes[attribute_key]).toEqual(attribute_value); + + for (const [attribute_key, attribute_value] of Object.entries(attribute_tuple)) { + expect(bedrockAttributes[attribute_key]).toEqual(attribute_value); + } + const bedrockAgentSuccessAttributes: Attributes = doResponseHookBedrock(services, 'BedrockAgent', operation); - expect(Object.entries(bedrockAgentSuccessAttributes).length).toBe(1); - expect(bedrockAgentSuccessAttributes[attribute_key]).toEqual(attribute_value); + + for (const [attribute_key, attribute_value] of Object.entries(attribute_tuple)) { + expect(bedrockAgentSuccessAttributes[attribute_key]).toEqual(attribute_value); + } } }); @@ -303,6 +410,55 @@ describe('InstrumentationPatchTest', () => { return doExtractAttributes(services, serviceName, params); } + function doExtractSNSAttributes(services: Map): Attributes { + const serviceName: string = 'SNS'; + const params: NormalizedRequest = { + serviceName: serviceName, + commandName: 'mockCommandName', + commandInput: { + TopicArn: _TOPIC_ARN, + }, + }; + return doExtractAttributes(services, serviceName, params); + } + + function doExtractLambdaAttributes(services: Map): Attributes { + const serviceName: string = 'Lambda'; + const params: NormalizedRequest = { + serviceName: serviceName, + commandName: 'mockCommandName', + commandInput: { + UUID: _UUID, + }, + }; + return doExtractAttributes(services, serviceName, params); + } + + function doExtractSFNAttributes(services: Map): Attributes { + const serviceName: string = 'SFN'; + const params: NormalizedRequest = { + serviceName: serviceName, + commandName: 'mockCommandName', + commandInput: { + stateMachineArn: _STATE_MACHINE_ARN, + activityArn: _ACTIVITY_ARN, + }, + }; + return doExtractAttributes(services, serviceName, params); + } + + function doExtractSecretsManagerAttributes(services: Map): Attributes { + const serviceName: string = 'SecretsManager'; + const params: NormalizedRequest = { + serviceName: serviceName, + commandName: 'mockCommandName', + commandInput: { + SecretId: _SECRETS_ARN, + }, + }; + return doExtractAttributes(services, serviceName, params); + } + function doExtractBedrockAttributes( services: Map, serviceName: string, @@ -335,6 +491,22 @@ describe('InstrumentationPatchTest', () => { return requestMetadata.spanAttributes || {}; } + function doResponseHookSecretsManager(services: Map): Attributes { + const results: Partial = { + data: { + ARN: _SECRETS_ARN, + }, + + request: { + commandInput: {}, + commandName: 'dummy_operation', + serviceName: 'SecretsManager', + }, + }; + + return doResponseHook(services, 'SecretsManager', results as NormalizedResponse); + } + function doResponseHookBedrock( services: Map, serviceName: string, @@ -346,6 +518,7 @@ describe('InstrumentationPatchTest', () => { dataSourceId: _BEDROCK_DATASOURCE_ID, knowledgeBaseId: _BEDROCK_KNOWLEDGEBASE_ID, guardrailId: _BEDROCK_GUARDRAIL_ID, + guardrailArn: _BEDROCK_GUARDRAIL_ARN, modelId: _GEN_AI_REQUEST_MODEL, }, request: { @@ -388,6 +561,52 @@ describe('InstrumentationPatchTest', () => { expect(filteredInstrumentations.length).toEqual(1); return filteredInstrumentations[0] as AwsLambdaInstrumentation; } + + describe('AwsSdkInstrumentationPatchTest', () => { + let s3: S3; + const region = 'us-east-1'; + + it('injects trace context header into request via propagator', async () => { + s3 = new S3({ + region: region, + credentials: { + accessKeyId: 'abcde', + secretAccessKey: 'abcde', + }, + }); + + const dummyBucketName: string = 'dummy-bucket-name'; + let reqHeaders: any = {}; + + nock(`https://${dummyBucketName}.s3.${region}.amazonaws.com`) + .get('/') + .reply(200, function (uri: any, requestBody: any) { + reqHeaders = this.req.headers; + return 'null'; + }); + + await s3 + .listObjects({ + Bucket: dummyBucketName, + }) + .catch((err: any) => {}); + + const testSpans: ReadableSpan[] = getTestSpans(); + const listObjectsSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'S3.ListObjects'; + }); + + expect(listObjectsSpans.length).toBe(1); + + const traceId = listObjectsSpans[0].spanContext().traceId; + const spanId = listObjectsSpans[0].spanContext().spanId; + expect(reqHeaders['x-amzn-trace-id'] as string).toEqual( + `Root=1-${traceId.substring(0, 8)}-${listObjectsSpans[0] + .spanContext() + .traceId.substring(8, 32)};Parent=${spanId};Sampled=1` + ); + }); + }); }); describe('customExtractor', () => { diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/test/register.patch.test.ts b/aws-distro-opentelemetry-node-autoinstrumentation/test/register.patch.test.ts deleted file mode 100644 index acf4f31..0000000 --- a/aws-distro-opentelemetry-node-autoinstrumentation/test/register.patch.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -// Modifications Copyright The OpenTelemetry Authors. Licensed under the Apache License 2.0 License. - -import * as assert from 'assert'; -import { spawnSync, SpawnSyncReturns } from 'child_process'; - -// The OpenTelemetry Authors code -describe('RegisterPatch', function () { - it('Correctly applies AWS SDK Patches and generates expected attributes for S3, Kinesis, and SQS Client calls', () => { - const proc: SpawnSyncReturns = spawnSync( - process.execPath, - ['--require', '../build/src/register.js', './test-app/app-aws-sdk-client-calls.js'], - { - cwd: __dirname, - timeout: 10000, - killSignal: 'SIGKILL', // SIGTERM is not sufficient to terminate some hangs - env: Object.assign({}, process.env, { - OTEL_NODE_RESOURCE_DETECTORS: 'none', - OTEL_TRACES_EXPORTER: 'console', - // nx (used by lerna run) defaults `FORCE_COLOR=true`, which in - // node v18.17.0, v20.3.0 and later results in ANSI color escapes - // in the ConsoleSpanExporter output that is checked below. - FORCE_COLOR: '0', - - OTEL_LOG_LEVEL: 'ALL', - OTEL_TRACES_SAMPLER: 'always_on', - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: 'http://localhost:4316/v1/traces', - OTEL_RESOURCE_ATTRIBUTES: 'service.name=test-adot-sdk-ec2-service-name', - OTEL_AWS_APPLICATION_SIGNALS_ENABLED: 'true', - OTEL_NODE_DISABLED_INSTRUMENTATIONS: 'fs', - }), - } - ); - assert.ifError(proc.error); - assert.equal(proc.status, 0, `proc.status (${proc.status})`); - assert.equal(proc.signal, null, `proc.signal (${proc.signal})`); - - assert.ok(proc.stdout.includes('AWS Distro of OpenTelemetry automatic instrumentation started successfully')); - assert.ok(proc.stdout.includes("Environment variable OTEL_EXPORTER_OTLP_PROTOCOL is set to 'http/protobuf'")); - assert.ok(proc.stdout.includes("Environment variable OTEL_PROPAGATORS is set to 'xray,tracecontext,b3,b3multi'")); - - assert.ok( - proc.stdout.includes("'service.name': 'test-adot-sdk-ec2-service-name'"), - 'console span output in stdout - validate service.name' - ); - - assert.ok( - proc.stdout.includes("'aws.s3.bucket': 'test-bucket-not-exists'"), - 'console span output in stdout - validate aws.s3.bucket' - ); - assert.ok( - proc.stdout.includes("'aws.remote.resource.type': 'AWS::S3::Bucket'"), - 'console span output in stdout - validate aws.remote.resource.type' - ); - assert.ok( - proc.stdout.includes("'aws.remote.resource.identifier': 'test-bucket-not-exists'"), - 'console span output in stdout - validate aws.remote.resource.identifier' - ); - - assert.ok( - proc.stdout.includes("'aws.kinesis.stream.name': 'my-kinesis-stream'"), - 'console span output in stdout - validate aws.kinesis.stream.name' - ); - assert.ok( - proc.stdout.includes("'aws.remote.resource.type': 'AWS::Kinesis::Stream'"), - 'console span output in stdout - validate aws.remote.resource.type' - ); - assert.ok( - proc.stdout.includes("'aws.remote.resource.identifier': 'my-kinesis-stream'"), - 'console span output in stdout - validate aws.remote.resource.identifier' - ); - - assert.ok( - proc.stdout.includes("'aws.sqs.queue.url': 'https://sqs.us-east-1.amazonaws.com/012345678910/sqs-queue-name'"), - 'console span output in stdout - validate aws.sqs.queue.url' - ); - assert.ok( - proc.stdout.includes("'aws.remote.resource.type': 'AWS::SQS::Queue'"), - 'console span output in stdout - validate aws.remote.resource.type' - ); - assert.ok( - proc.stdout.includes("'aws.remote.resource.identifier': 'sqs-queue-name'"), - 'console span output in stdout - validate aws.remote.resource.identifier' - ); - }); -}); -// END The OpenTelemetry Authors code diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/test/test-app/app-aws-sdk-client-calls.js b/aws-distro-opentelemetry-node-autoinstrumentation/test/test-app/app-aws-sdk-client-calls.js deleted file mode 100644 index c9d06c8..0000000 --- a/aws-distro-opentelemetry-node-autoinstrumentation/test/test-app/app-aws-sdk-client-calls.js +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Used in register.patch.test.ts to mimic a JS app using SQS client of AWS SDK for JS (v3). -const { S3Client, ListObjectsCommand } = require("@aws-sdk/client-s3"); -const { KinesisClient, ListStreamsCommand } = require('@aws-sdk/client-kinesis'); -const { SQSClient, GetQueueAttributesCommand } = require("@aws-sdk/client-sqs"); - -const s3Client = new S3Client({}); -const bucketName = "test-bucket-not-exists"; - -const kinesisClient = new KinesisClient({}); -const streamName = "my-kinesis-stream"; - -const sqsClient = new SQSClient({}); -const queueUrl = "https://sqs.us-east-1.amazonaws.com/012345678910/sqs-queue-name"; - -const awsSdkClientSendPromises = [ - s3Client.send( - new ListObjectsCommand({ - Bucket: bucketName - }) - ), - kinesisClient.send( - new ListStreamsCommand({ - StreamName: streamName, - }) - ), - sqsClient.send( - new GetQueueAttributesCommand({ - QueueUrl: queueUrl - }) - ), -] - -Promise.all(awsSdkClientSendPromises).catch(e => { - console.error("Exception thrown", e.message); -}); diff --git a/checksum.txt b/checksum.txt deleted file mode 100644 index e69de29..0000000 diff --git a/contract-tests/images/applications/aws-sdk/package.json b/contract-tests/images/applications/aws-sdk/package.json index cef37d8..8596d51 100644 --- a/contract-tests/images/applications/aws-sdk/package.json +++ b/contract-tests/images/applications/aws-sdk/package.json @@ -15,10 +15,16 @@ "@aws-sdk/client-bedrock-agent-runtime": "^3.676.0", "@aws-sdk/client-bedrock-runtime": "^3.675.0", "@aws-sdk/client-dynamodb": "^3.658.1", + "@aws-sdk/client-iam": "^3.696.0", "@aws-sdk/client-kinesis": "^3.658.1", + "@aws-sdk/client-lambda": "^3.698.0", "@aws-sdk/client-s3": "^3.658.1", + "@aws-sdk/client-secrets-manager": "^3.696.0", + "@aws-sdk/client-sfn": "^3.696.0", + "@aws-sdk/client-sns": "^3.696.0", "@aws-sdk/client-sqs": "^3.658.1", "@smithy/node-http-handler": "^3.2.3", + "jszip": "^3.10.1", "node-fetch": "^2.7.0" } } diff --git a/contract-tests/images/applications/aws-sdk/server.js b/contract-tests/images/applications/aws-sdk/server.js index 0a84155..288bb96 100644 --- a/contract-tests/images/applications/aws-sdk/server.js +++ b/contract-tests/images/applications/aws-sdk/server.js @@ -5,16 +5,22 @@ const fs = require('fs'); const os = require('os'); const ospath = require('path'); const { NodeHttpHandler } =require('@smithy/node-http-handler'); +const fetch = require('node-fetch'); +const JSZip = require('jszip'); const { S3Client, CreateBucketCommand, PutObjectCommand, GetObjectCommand } = require('@aws-sdk/client-s3'); const { DynamoDBClient, CreateTableCommand, PutItemCommand } = require('@aws-sdk/client-dynamodb'); const { SQSClient, CreateQueueCommand, SendMessageCommand, ReceiveMessageCommand } = require('@aws-sdk/client-sqs'); const { KinesisClient, CreateStreamCommand, PutRecordCommand } = require('@aws-sdk/client-kinesis'); -const fetch = require('node-fetch'); const { BedrockClient, GetGuardrailCommand } = require('@aws-sdk/client-bedrock'); const { BedrockAgentClient, GetKnowledgeBaseCommand, GetDataSourceCommand, GetAgentCommand } = require('@aws-sdk/client-bedrock-agent'); const { BedrockRuntimeClient, InvokeModelCommand } = require('@aws-sdk/client-bedrock-runtime'); const { BedrockAgentRuntimeClient, InvokeAgentCommand, RetrieveCommand } = require('@aws-sdk/client-bedrock-agent-runtime'); +const { SNSClient, CreateTopicCommand, GetTopicAttributesCommand } = require('@aws-sdk/client-sns'); +const { SecretsManagerClient, CreateSecretCommand, DescribeSecretCommand } = require('@aws-sdk/client-secrets-manager'); +const { SFNClient, CreateStateMachineCommand, CreateActivityCommand, DescribeStateMachineCommand, DescribeActivityCommand } = require('@aws-sdk/client-sfn'); +const { IAMClient, AttachRolePolicyCommand, CreateRoleCommand } = require('@aws-sdk/client-iam') +const { LambdaClient, CreateFunctionCommand, GetEventSourceMappingCommand, CreateEventSourceMappingCommand, UpdateEventSourceMappingCommand } = require('@aws-sdk/client-lambda'); const _PORT = 8080; @@ -26,6 +32,7 @@ const _AWS_SDK_ENDPOINT = process.env.AWS_SDK_ENDPOINT; const _AWS_REGION = process.env.AWS_REGION; const _FAULT_ENDPOINT = 'http://fault.test:8080'; + process.env.AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID || 'testcontainers-localstack'; process.env.AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY || 'testcontainers-localstack'; @@ -64,6 +71,16 @@ async function prepareAwsServer() { region: _AWS_REGION, }); + const secretsClient = new SecretsManagerClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }) + + const snsClient = new SNSClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }) + // Set up S3 await s3Client.send( new CreateBucketCommand({ @@ -103,7 +120,7 @@ async function prepareAwsServer() { ); // Set up SQS - await sqsClient.send( + const sqsQueue = await sqsClient.send( new CreateQueueCommand({ QueueName: 'test_put_get_queue', }) @@ -114,11 +131,32 @@ async function prepareAwsServer() { new CreateStreamCommand({ StreamName: 'test_stream', ShardCount: 1, + })) + + // Set up SecretsManager + await secretsClient.send( + new CreateSecretCommand({ + "Description": "My test secret", + "Name": "MyTestSecret", + "SecretString": "{\"username\":\"user\",\"password\":\"password\"}" }) ); + + // Set up SNS + await snsClient.send(new CreateTopicCommand({ + "Name": "TestTopic" + })) + + // Set up Lambda + await setupLambda() + + // Set up StepFunctions + await setupSfn() + } catch (error) { console.error('Unexpected exception occurred', error); } + } const server = http.createServer(async (req, res) => { @@ -135,7 +173,7 @@ const server = http.createServer(async (req, res) => { res.writeHead(405); res.end(); } -}); +}) async function handleGetRequest(req, res, path) { if (path.includes('s3')) { @@ -148,6 +186,14 @@ async function handleGetRequest(req, res, path) { await handleKinesisRequest(req, res, path); } else if (path.includes('bedrock')) { await handleBedrockRequest(req, res, path); + } else if (path.includes('secretsmanager')) { + await handleSecretsRequest(req, res, path); + } else if (path.includes('stepfunctions')) { + await handleSfnRequest(req, res, path); + } else if (path.includes('sns')) { + await handleSnsRequest(req, res, path); + } else if (path.includes('lambda')) { + await handleLambdaRequest(req, res, path); } else { res.writeHead(404); res.end(); @@ -518,7 +564,9 @@ async function handleBedrockRequest(req, res, path) { await withInjected200Success( bedrockClient, ['GetGuardrailCommand'], - { guardrailId: 'bt4o77i015cu' }, + { guardrailId: 'bt4o77i015cu', + guardrailArn: 'arn:aws:bedrock:us-east-1:000000000000:guardrail/bt4o77i015cu' + }, async () => { await bedrockClient.send( new GetGuardrailCommand({ @@ -553,30 +601,212 @@ async function handleBedrockRequest(req, res, path) { }); res.statusCode = 200; } else if (path.includes('invokemodel/invoke-model')) { - await withInjected200Success(bedrockRuntimeClient, ['InvokeModelCommand'], {}, async () => { - const modelId = 'amazon.titan-text-premier-v1:0'; - const userMessage = "Describe the purpose of a 'hello world' program in one line."; - const prompt = `[INST] ${userMessage} [/INST]`; - - const body = JSON.stringify({ - inputText: prompt, - textGenerationConfig: { - maxTokenCount: 3072, - stopSequences: [], - temperature: 0.7, - topP: 0.9, - }, - }); + const get_model_request_response = function () { + const prompt = "Describe the purpose of a 'hello world' program in one line."; + let modelId = '' + let request_body = {} + let response_body = {} + + if (path.includes('amazon.titan')) { + + modelId = 'amazon.titan-text-premier-v1:0'; + + request_body = { + inputText: prompt, + textGenerationConfig: { + maxTokenCount: 3072, + stopSequences: [], + temperature: 0.7, + topP: 0.9, + }, + }; + + response_body = { + inputTextTokenCount: 15, + results: [ + { + tokenCount: 13, + outputText: 'text-test-response', + completionReason: 'CONTENT_FILTERED', + }, + ], + } + } + + if (path.includes("amazon.nova")) { + + modelId = "amazon.nova-pro-v1:0" + + request_body = { + messages: [{role: "user", content: [{text: "A camping trip"}]}], + inferenceConfig: { + max_new_tokens: 800, + temperature: 0.9, + top_p: 0.7, + }, + } + + response_body = { + output: {message: {content: [{text: ""}], role: "assistant"}}, + stopReason: "max_tokens", + usage: { + inputTokens: 432, + outputTokens: 681 + }, + } + } + + if (path.includes('anthropic.claude')) { + + modelId = 'anthropic.claude-v2:1'; + + request_body = { + anthropic_version: 'bedrock-2023-05-31', + max_tokens: 1000, + temperature: 0.99, + top_p: 1, + messages: [ + { + role: 'user', + content: [{ type: 'text', text: prompt }], + }, + ], + }; + + response_body = { + stop_reason: 'end_turn', + usage: { + input_tokens: 15, + output_tokens: 13, + }, + } + } + + if (path.includes('meta.llama')) { + modelId = 'meta.llama2-13b-chat-v1'; + + request_body = { + prompt, + max_gen_len: 512, + temperature: 0.5, + top_p: 0.9 + }; + + response_body = { + prompt_token_count: 31, + generation_token_count: 49, + stop_reason: 'stop' + } + } + + if (path.includes('cohere.command')) { + modelId = 'cohere.command-light-text-v14'; + + request_body = { + prompt, + max_tokens: 512, + temperature: 0.5, + p: 0.65, + }; + + response_body = { + generations: [ + { + finish_reason: 'COMPLETE', + text: 'test-generation-text', + }, + ], + prompt: prompt, + }; + } + + if (path.includes('cohere.command-r')) { + modelId = 'cohere.command-r-v1:0'; + + request_body = { + message: prompt, + max_tokens: 512, + temperature: 0.5, + p: 0.65, + }; + + response_body = { + finish_reason: 'COMPLETE', + text: 'test-generation-text', + prompt: prompt, + request: { + commandInput: { + modelId: modelId, + }, + }, + } + } + + if (path.includes('ai21.jamba')) { + modelId = 'ai21.jamba-1-5-large-v1:0'; + + request_body = { + messages: [ + { + role: 'user', + content: prompt, + }, + ], + top_p: 0.8, + temperature: 0.6, + max_tokens: 512, + }; + + response_body = { + stop_reason: 'end_turn', + usage: { + prompt_tokens: 21, + completion_tokens: 24, + }, + choices: [ + { + finish_reason: 'stop', + }, + ], + } + } + + if (path.includes('mistral')) { + modelId = 'mistral.mistral-7b-instruct-v0:2'; + + request_body = { + prompt, + max_tokens: 4096, + temperature: 0.75, + top_p: 0.99, + }; + + response_body = { + outputs: [ + { + text: 'test-output-text', + stop_reason: 'stop', + }, + ] + } + } + + return [modelId, JSON.stringify(request_body), new TextEncoder().encode(JSON.stringify(response_body))] + } + + const [modelId, request_body, response_body] = get_model_request_response(); + await withInjected200Success(bedrockRuntimeClient, ['InvokeModelCommand'], { body: response_body }, async () => { await bedrockRuntimeClient.send( new InvokeModelCommand({ - body: body, + body: request_body, modelId: modelId, accept: 'application/json', contentType: 'application/json', }) ); }); + res.statusCode = 200; } else { res.statusCode = 404; @@ -589,6 +819,349 @@ async function handleBedrockRequest(req, res, path) { res.end(); } + +async function handleSecretsRequest(req, res, path) { + const secretsClient = new SecretsManagerClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }); + + if (path.includes(_ERROR)) { + res.statusCode = 400; + + try { + await secretsClient.send( + new DescribeSecretCommand({ + SecretId: "arn:aws:secretsmanager:us-west-2:000000000000:secret:nonExistentSecret" + }) + ); + } catch (err) { + console.log('Expected exception occurred', err); + } + } + + if (path.includes(_FAULT)) { + res.statusCode = 500; + statusCodeForFault = 500; + + try { + const faultSecretsClient = new SecretsManagerClient({ + endpoint: _FAULT_ENDPOINT, + region: _AWS_REGION, + ...noRetryConfig, + }); + + await faultSecretsClient.send( + new DescribeSecretCommand({ + SecretId: "arn:aws:secretsmanager:us-west-2:000000000000:secret:nonExistentSecret" + }) + ); + } catch (err) { + console.log('Expected exception occurred', err); + } + } + + if (path.includes('describesecret/my-secret')) { + await secretsClient.send( + new DescribeSecretCommand({ + SecretId: "MyTestSecret" + }) + ); + } + + res.end(); +} + +async function handleSfnRequest(req, res, path) { + const sfnClient = new SFNClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }); + + if (path.includes(_ERROR)) { + res.statusCode = 400; + + try { + await sfnClient.send( + new DescribeStateMachineCommand({ + stateMachineArn: "arn:aws:states:us-west-2:000000000000:stateMachine:nonExistentStateMachine" + }) + ); + } catch (err) { + console.log('Expected exception occurred', err); + } + } + + if (path.includes(_FAULT)) { + res.statusCode = 500; + statusCodeForFault = 500; + + try { + + const faultSfnClient = new SFNClient({ + endpoint: _FAULT_ENDPOINT, + region: _AWS_REGION, + ...noRetryConfig, + }); + + await faultSfnClient.send( + new DescribeStateMachineCommand({ + stateMachineArn: "arn:aws:states:us-west-2:000000000000:stateMachine:invalid-state-machine" + }) + ); + } catch (err) { + console.log('Expected exception occurred', err); + } + } + + if (path.includes('describestatemachine/state-machine')) { + await sfnClient.send( + new DescribeStateMachineCommand({ + stateMachineArn: "arn:aws:states:us-west-2:000000000000:stateMachine:TestStateMachine" + }) + ); + } + + if (path.includes('describeactivity/activity')) { + await sfnClient.send( + new DescribeActivityCommand({ + activityArn: "arn:aws:states:us-west-2:000000000000:activity:TestActivity" + }) + ); + } + + res.end(); +} + +async function handleSnsRequest(req, res, path) { + const snsClient = new SNSClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }); + + if (path.includes(_ERROR)) { + res.statusCode = 404; + + try { + await snsClient.send( + new GetTopicAttributesCommand({ + TopicArn: "arn:aws:sns:us-west-2:000000000000:nonExistentTopic", + }) + ); + } catch (err) { + console.log('Expected exception occurred', err); + } + + } + + if (path.includes(_FAULT)) { + res.statusCode = 500; + statusCodeForFault = 500; + + try { + const faultSnsClient = new SNSClient({ + endpoint: _FAULT_ENDPOINT, + region: _AWS_REGION, + ...noRetryConfig, + }); + + await faultSnsClient.send( + new GetTopicAttributesCommand({ + TopicArn: "arn:aws:sns:us-west-2:000000000000:invalidTopic" + }) + ); + } catch (err) { + console.log('Expected exception occurred', err); + } + } + + if (path.includes('gettopicattributes/topic')) { + await snsClient.send( + new GetTopicAttributesCommand({ + TopicArn: "arn:aws:sns:us-west-2:000000000000:TestTopic" + }) + ); + } + + res.end(); +} + +async function handleLambdaRequest(req, res, path) { + const lambdaClient = new LambdaClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }); + + if (path.includes(_ERROR)) { + res.statusCode = 404; + + try { + await lambdaClient.send( + new GetEventSourceMappingCommand({ + UUID: "nonExistentUUID" + }) + ); + } + catch(err) { + console.log('Expected exception occurred', err); + } + + } + + if (path.includes(_FAULT)) { + res.statusCode = 500; + statusCodeForFault = 500; + + try { + const faultLambdaClient = new LambdaClient({ + endpoint: _FAULT_ENDPOINT, + region: _AWS_REGION, + ...noRetryConfig, + }); + + await faultLambdaClient.send( + new UpdateEventSourceMappingCommand({ + UUID: "123e4567-e89b-12d3-a456-426614174000" + }) + ); + } + catch(err) { + console.log('Expected exception occurred', err); + } + } + + + if (path.includes('geteventsourcemapping')) { + await lambdaClient.send( + new GetEventSourceMappingCommand({ + UUID: '' + }) + ); + } + res.end(); +} + +async function setupLambda() { + const lambdaClient = new LambdaClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }); + + const iamClient = new IAMClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }); + + const trustPolicy = { + Version: "2012-10-17", + Statement: [{ + Effect: "Allow", + Principal: { + Service: "lambda.amazonaws.com" + }, + Action: "sts:AssumeRole" + }] + }; + + const functionName = 'testFunction' + + const lambdaRoleParams = { + RoleName: "LambdaRole", + AssumeRolePolicyDocument: JSON.stringify(trustPolicy), + }; + + const policyParams = { + RoleName: "LambdaRole", + PolicyArn: "arn:aws:iam::aws:policy/AWSLambda_FullAccess" + }; + + const role = await iamClient.send(new CreateRoleCommand(lambdaRoleParams)); + await iamClient.send(new AttachRolePolicyCommand(policyParams)); + + const zip = new JSZip(); + zip.file('index.js', 'exports.handler = async (event) => { return { statusCode: 200 }; };'); + const zipBuffer = await zip.generateAsync({ type: 'nodebuffer' }); + + const functionParams = { + Code: { + ZipFile: zipBuffer + }, + FunctionName: functionName, + Handler: "index.handler", + Role: role.Role.Arn, + Runtime: "nodejs18.x" + }; + + const mappingParams = { + EventSourceArn: "arn:aws:sns:us-west-2:000000000000:TestTopic", + FunctionName: functionName, + Enabled: false + } + + await lambdaClient.send(new CreateFunctionCommand(functionParams)); + await lambdaClient.send(new CreateEventSourceMappingCommand(mappingParams)); +} + +async function setupSfn() { + const sfnClient = new SFNClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }); + + const iamClient = new IAMClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }); + + const trustPolicy = { + Version: "2012-10-17", + Statement: [{ + Effect: "Allow", + Principal: { + Service: "states.amazonaws.com" + }, + Action: "sts:AssumeRole" + }] +}; + +const roleName = 'testRole' + +const createRoleResponse = await iamClient.send(new CreateRoleCommand({ + RoleName: roleName, + AssumeRolePolicyDocument: JSON.stringify(trustPolicy), +})); + +await iamClient.send(new AttachRolePolicyCommand({ + RoleName: roleName, + PolicyArn: 'arn:aws:iam::aws:policy/AWSStepFunctionsFullAccess' +})); + +const roleArn = createRoleResponse.Role.Arn + +const definition = { + StartAt: "HelloWorld", + States: { + "HelloWorld": { + Type: "Pass", + Result: "Hello, World!", + End: true + } + } +}; + +await sfnClient.send(new CreateStateMachineCommand({ + name: 'TestStateMachine', + definition: JSON.stringify(definition), + roleArn: roleArn, + type: 'STANDARD' +})); + +await sfnClient.send( + new CreateActivityCommand({ + name: 'TestActivity', + })); +} + function inject200Success(client, commandNames, additionalResponse = {}, middlewareName = 'inject200SuccessMiddleware') { const middleware = (next, context) => async (args) => { const { commandName } = context; @@ -616,11 +1189,9 @@ async function withInjected200Success(client, commandNames, additionalResponse, client.middlewareStack.remove(middlewareName); } - - prepareAwsServer().then(() => { server.listen(_PORT, '0.0.0.0', () => { console.log('Server is listening on port', _PORT); console.log('Ready'); }); -}); +}); \ No newline at end of file diff --git a/contract-tests/images/applications/mysql2/package.json b/contract-tests/images/applications/mysql2/package.json index edf0fcd..5a18ef9 100644 --- a/contract-tests/images/applications/mysql2/package.json +++ b/contract-tests/images/applications/mysql2/package.json @@ -10,6 +10,6 @@ "license": "ISC", "description": "", "dependencies": { - "mysql2": "^3.11.3" + "mysql2": "3.11.4" } -} +} \ No newline at end of file diff --git a/contract-tests/tests/test/amazon/aws-sdk/aws_sdk_test.py b/contract-tests/tests/test/amazon/aws-sdk/aws_sdk_test.py index bb2ce3c..005e286 100644 --- a/contract-tests/tests/test/amazon/aws-sdk/aws_sdk_test.py +++ b/contract-tests/tests/test/amazon/aws-sdk/aws_sdk_test.py @@ -1,6 +1,8 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 from logging import INFO, Logger, getLogger +import math +import re from typing import Dict, List from docker.types import EndpointConfig @@ -17,6 +19,7 @@ AWS_REMOTE_RESOURCE_TYPE, AWS_REMOTE_SERVICE, AWS_SPAN_KIND, + AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER ) from opentelemetry.proto.common.v1.common_pb2 import AnyValue, KeyValue from opentelemetry.proto.metrics.v1.metrics_pb2 import ExponentialHistogramDataPoint, Metric @@ -31,10 +34,21 @@ _AWS_KINESIS_STREAM_NAME: str = "aws.kinesis.stream.name" _AWS_BEDROCK_AGENT_ID: str = "aws.bedrock.agent.id" _AWS_BEDROCK_GUARDRAIL_ID: str = "aws.bedrock.guardrail.id" +_AWS_BEDROCK_GUARDRAIL_ARN: str = "aws.bedrock.guardrail.arn" _AWS_BEDROCK_KNOWLEDGE_BASE_ID: str = "aws.bedrock.knowledge_base.id" _AWS_BEDROCK_DATA_SOURCE_ID: str = "aws.bedrock.data_source.id" +_AWS_SECRET_ARN: str = "aws.secretsmanager.secret.arn" +_AWS_SNS_TOPIC_ARN: str = 'aws.sns.topic.arn' +_AWS_LAMBDA_RESOURCE_MAPPING_ID: str = 'aws.lambda.resource_mapping.id' +_AWS_STATE_MACHINE_ARN: str = "aws.stepfunctions.state_machine.arn" +_AWS_ACTIVITY_ARN: str = "aws.stepfunctions.activity.arn" _GEN_AI_REQUEST_MODEL: str = "gen_ai.request.model" - +_GEN_AI_REQUEST_TEMPERATURE: str = "gen_ai.request.temperature" +_GEN_AI_REQUEST_TOP_P: str = "gen_ai.request.top_p" +_GEN_AI_REQUEST_MAX_TOKENS: str = "gen_ai.request.max_tokens" +_GEN_AI_RESPONSE_FINISH_REASONS: str = "gen_ai.response.finish_reasons" +_GEN_AI_USAGE_INPUT_TOKENS: str = 'gen_ai.usage.input_tokens' +_GEN_AI_USAGE_OUTPUT_TOKENS: str = 'gen_ai.usage.output_tokens' # pylint: disable=too-many-public-methods class AWSSDKTest(ContractTestBase): @@ -71,8 +85,9 @@ def set_up_dependency_container(cls): cls._local_stack: LocalStackContainer = ( LocalStackContainer(image="localstack/localstack:3.5.0") .with_name("localstack") - .with_services("s3", "sqs", "dynamodb", "kinesis") + .with_services("s3", "sqs", "dynamodb", "kinesis", 'secretsmanager', 'stepfunctions', 'iam', 'sns', "lambda") .with_env("DEFAULT_REGION", "us-west-2") + .with_volume_mapping("/var/run/docker.sock", "/var/run/docker.sock") .with_kwargs(network=NETWORK_NAME, networking_config=local_stack_networking_config) ) cls._local_stack.start() @@ -98,6 +113,7 @@ def test_s3_create_bucket(self): remote_operation="CreateBucket", remote_resource_type="AWS::S3::Bucket", remote_resource_identifier="test-bucket-name", + cloudformation_primary_identifier="test-bucket-name", request_specific_attributes={ SpanAttributes.AWS_S3_BUCKET: "test-bucket-name", }, @@ -116,6 +132,7 @@ def test_s3_create_object(self): remote_operation="PutObject", remote_resource_type="AWS::S3::Bucket", remote_resource_identifier="test-put-object-bucket-name", + cloudformation_primary_identifier="test-put-object-bucket-name", request_specific_attributes={ SpanAttributes.AWS_S3_BUCKET: "test-put-object-bucket-name", }, @@ -134,6 +151,7 @@ def test_s3_get_object(self): remote_operation="GetObject", remote_resource_type="AWS::S3::Bucket", remote_resource_identifier="test-get-object-bucket-name", + cloudformation_primary_identifier="test-get-object-bucket-name", request_specific_attributes={ SpanAttributes.AWS_S3_BUCKET: "test-get-object-bucket-name", }, @@ -152,6 +170,7 @@ def test_s3_error(self): remote_operation="CreateBucket", remote_resource_type="AWS::S3::Bucket", remote_resource_identifier="-", + cloudformation_primary_identifier="-", request_specific_attributes={ SpanAttributes.AWS_S3_BUCKET: "-", }, @@ -172,6 +191,7 @@ def test_s3_fault(self): remote_operation="CreateBucket", remote_resource_type="AWS::S3::Bucket", remote_resource_identifier="valid-bucket-name", + cloudformation_primary_identifier="valid-bucket-name", request_specific_attributes={ SpanAttributes.AWS_S3_BUCKET: "valid-bucket-name", }, @@ -190,6 +210,7 @@ def test_dynamodb_create_table(self): remote_operation="CreateTable", remote_resource_type="AWS::DynamoDB::Table", remote_resource_identifier="test_table", + cloudformation_primary_identifier="test_table", request_specific_attributes={ SpanAttributes.AWS_DYNAMODB_TABLE_NAMES: ["test_table"], }, @@ -208,6 +229,7 @@ def test_dynamodb_put_item(self): remote_operation="PutItem", remote_resource_type="AWS::DynamoDB::Table", remote_resource_identifier="put_test_table", + cloudformation_primary_identifier="put_test_table", request_specific_attributes={ SpanAttributes.AWS_DYNAMODB_TABLE_NAMES: ["put_test_table"], }, @@ -226,6 +248,7 @@ def test_dynamodb_error(self): remote_operation="PutItem", remote_resource_type="AWS::DynamoDB::Table", remote_resource_identifier="invalid_table", + cloudformation_primary_identifier="invalid_table", request_specific_attributes={ SpanAttributes.AWS_DYNAMODB_TABLE_NAMES: ["invalid_table"], }, @@ -246,6 +269,7 @@ def test_dynamodb_fault(self): remote_operation="PutItem", remote_resource_type="AWS::DynamoDB::Table", remote_resource_identifier="invalid_table", + cloudformation_primary_identifier="invalid_table", request_specific_attributes={ SpanAttributes.AWS_DYNAMODB_TABLE_NAMES: ["invalid_table"], }, @@ -264,6 +288,7 @@ def test_sqs_create_queue(self): remote_operation="CreateQueue", remote_resource_type="AWS::SQS::Queue", remote_resource_identifier="test_queue", + cloudformation_primary_identifier="test_queue", request_specific_attributes={ _AWS_SQS_QUEUE_NAME: "test_queue", }, @@ -283,6 +308,7 @@ def test_sqs_send_message(self): remote_operation="SendMessage", remote_resource_type="AWS::SQS::Queue", remote_resource_identifier="test_put_get_queue", + cloudformation_primary_identifier="http://localstack:4566/000000000000/test_put_get_queue", request_specific_attributes={ _AWS_SQS_QUEUE_URL: "http://localstack:4566/000000000000/test_put_get_queue", }, @@ -303,6 +329,7 @@ def test_sqs_receive_message(self): remote_operation="ReceiveMessage", remote_resource_type="AWS::SQS::Queue", remote_resource_identifier="test_put_get_queue", + cloudformation_primary_identifier="http://localstack:4566/000000000000/test_put_get_queue", request_specific_attributes={ _AWS_SQS_QUEUE_URL: "http://localstack:4566/000000000000/test_put_get_queue", }, @@ -323,6 +350,7 @@ def test_sqs_error(self): remote_operation="SendMessage", remote_resource_type="AWS::SQS::Queue", remote_resource_identifier="sqserror", + cloudformation_primary_identifier="http://error.test:8080/000000000000/sqserror", request_specific_attributes={ _AWS_SQS_QUEUE_URL: "http://error.test:8080/000000000000/sqserror", }, @@ -344,6 +372,7 @@ def test_sqs_fault(self): remote_operation="CreateQueue", remote_resource_type="AWS::SQS::Queue", remote_resource_identifier="invalid_test", + cloudformation_primary_identifier="invalid_test", request_specific_attributes={ _AWS_SQS_QUEUE_NAME: "invalid_test", }, @@ -362,6 +391,7 @@ def test_kinesis_put_record(self): remote_operation="PutRecord", remote_resource_type="AWS::Kinesis::Stream", remote_resource_identifier="test_stream", + cloudformation_primary_identifier="test_stream", request_specific_attributes={ _AWS_KINESIS_STREAM_NAME: "test_stream", }, @@ -380,6 +410,7 @@ def test_kinesis_error(self): remote_operation="PutRecord", remote_resource_type="AWS::Kinesis::Stream", remote_resource_identifier="invalid_stream", + cloudformation_primary_identifier="invalid_stream", request_specific_attributes={ _AWS_KINESIS_STREAM_NAME: "invalid_stream", }, @@ -400,15 +431,74 @@ def test_kinesis_fault(self): remote_operation="PutRecord", remote_resource_type="AWS::Kinesis::Stream", remote_resource_identifier="test_stream", + cloudformation_primary_identifier="test_stream", request_specific_attributes={ _AWS_KINESIS_STREAM_NAME: "test_stream", }, span_name="Kinesis.PutRecord", ) - def test_bedrock_runtime_invoke_model(self): + def test_bedrock_runtime_invoke_model_amazon_titan(self): + result = self.do_test_requests( + "bedrock/invokemodel/invoke-model/amazon.titan-text-premier-v1:0", + "GET", + 200, + 0, + 0, + local_operation="GET /bedrock", + rpc_service="BedrockRuntime", + remote_service="AWS::BedrockRuntime", + remote_operation="InvokeModel", + remote_resource_type="AWS::Bedrock::Model", + remote_resource_identifier='amazon.titan-text-premier-v1:0', + cloudformation_primary_identifier="amazon.titan-text-premier-v1:0", + request_specific_attributes={ + _GEN_AI_REQUEST_MODEL: 'amazon.titan-text-premier-v1:0', + _GEN_AI_REQUEST_MAX_TOKENS: 3072, + _GEN_AI_REQUEST_TEMPERATURE: 0.7, + _GEN_AI_REQUEST_TOP_P: 0.9 + }, + response_specific_attributes={ + _GEN_AI_RESPONSE_FINISH_REASONS: ['CONTENT_FILTERED'], + _GEN_AI_USAGE_INPUT_TOKENS: 15, + _GEN_AI_USAGE_OUTPUT_TOKENS: 13 + }, + + span_name="BedrockRuntime.InvokeModel" + ) + + def test_bedrock_runtime_invoke_model_amazon_nova(self): + result = self.do_test_requests( + "bedrock/invokemodel/invoke-model/amazon.nova-pro-v1:0", + "GET", + 200, + 0, + 0, + local_operation="GET /bedrock", + rpc_service="BedrockRuntime", + remote_service="AWS::BedrockRuntime", + remote_operation="InvokeModel", + remote_resource_type="AWS::Bedrock::Model", + remote_resource_identifier='amazon.nova-pro-v1:0', + cloudformation_primary_identifier="amazon.nova-pro-v1:0", + request_specific_attributes={ + _GEN_AI_REQUEST_MODEL: 'amazon.nova-pro-v1:0', + _GEN_AI_REQUEST_MAX_TOKENS: 800, + _GEN_AI_REQUEST_TEMPERATURE: 0.9, + _GEN_AI_REQUEST_TOP_P: 0.7 + }, + response_specific_attributes={ + _GEN_AI_RESPONSE_FINISH_REASONS: ['max_tokens'], + _GEN_AI_USAGE_INPUT_TOKENS: 432, + _GEN_AI_USAGE_OUTPUT_TOKENS: 681 + }, + + span_name="BedrockRuntime.InvokeModel" + ) + + def test_bedrock_runtime_invoke_model_anthropic_claude(self): self.do_test_requests( - "bedrock/invokemodel/invoke-model", + "bedrock/invokemodel/invoke-model/anthropic.claude-v2:1", "GET", 200, 0, @@ -418,13 +508,163 @@ def test_bedrock_runtime_invoke_model(self): remote_service="AWS::BedrockRuntime", remote_operation="InvokeModel", remote_resource_type="AWS::Bedrock::Model", - remote_resource_identifier="amazon.titan-text-premier-v1:0", + remote_resource_identifier='anthropic.claude-v2:1', + cloudformation_primary_identifier="anthropic.claude-v2:1", request_specific_attributes={ - _GEN_AI_REQUEST_MODEL: "amazon.titan-text-premier-v1:0", - }, - span_name="BedrockRuntime.InvokeModel", + _GEN_AI_REQUEST_MODEL: 'anthropic.claude-v2:1', + _GEN_AI_REQUEST_MAX_TOKENS: 1000, + _GEN_AI_REQUEST_TEMPERATURE: 0.99, + _GEN_AI_REQUEST_TOP_P: 1 + }, + response_specific_attributes={ + _GEN_AI_RESPONSE_FINISH_REASONS: ['end_turn'], + _GEN_AI_USAGE_INPUT_TOKENS: 15, + _GEN_AI_USAGE_OUTPUT_TOKENS: 13 + }, + span_name="BedrockRuntime.InvokeModel" ) + def test_bedrock_runtime_invoke_model_meta_llama(self): + self.do_test_requests( + "bedrock/invokemodel/invoke-model/meta.llama2-13b-chat-v1", + "GET", + 200, + 0, + 0, + local_operation="GET /bedrock", + rpc_service="BedrockRuntime", + remote_service="AWS::BedrockRuntime", + remote_operation="InvokeModel", + remote_resource_type="AWS::Bedrock::Model", + remote_resource_identifier='meta.llama2-13b-chat-v1', + cloudformation_primary_identifier="meta.llama2-13b-chat-v1", + request_specific_attributes={ + _GEN_AI_REQUEST_MODEL: 'meta.llama2-13b-chat-v1', + _GEN_AI_REQUEST_MAX_TOKENS: 512, + _GEN_AI_REQUEST_TEMPERATURE: 0.5, + _GEN_AI_REQUEST_TOP_P: 0.9 + }, + response_specific_attributes={ + _GEN_AI_RESPONSE_FINISH_REASONS: ['stop'], + _GEN_AI_USAGE_INPUT_TOKENS: 31, + _GEN_AI_USAGE_OUTPUT_TOKENS: 49 + }, + span_name="BedrockRuntime.InvokeModel" + ) + + def test_bedrock_runtime_invoke_model_cohere_command_r(self): + self.do_test_requests( + "bedrock/invokemodel/invoke-model/cohere.command-r-v1:0", + "GET", + 200, + 0, + 0, + local_operation="GET /bedrock", + rpc_service="BedrockRuntime", + remote_service="AWS::BedrockRuntime", + remote_operation="InvokeModel", + remote_resource_type="AWS::Bedrock::Model", + remote_resource_identifier='cohere.command-r-v1:0', + cloudformation_primary_identifier="cohere.command-r-v1:0", + request_specific_attributes={ + _GEN_AI_REQUEST_MODEL: 'cohere.command-r-v1:0', + _GEN_AI_REQUEST_MAX_TOKENS: 512, + _GEN_AI_REQUEST_TEMPERATURE: 0.5, + _GEN_AI_REQUEST_TOP_P: 0.65 + }, + response_specific_attributes={ + _GEN_AI_RESPONSE_FINISH_REASONS: ['COMPLETE'], + _GEN_AI_USAGE_INPUT_TOKENS: math.ceil(len("Describe the purpose of a 'hello world' program in one line.") / 6), + _GEN_AI_USAGE_OUTPUT_TOKENS: math.ceil(len("test-generation-text") / 6) + }, + span_name="BedrockRuntime.InvokeModel" + ) + + # Delete once this model is fully deprecated on node + def test_bedrock_runtime_invoke_model_cohere_command(self): + self.do_test_requests( + "bedrock/invokemodel/invoke-model/cohere.command-light-text-v14", + "GET", + 200, + 0, + 0, + local_operation="GET /bedrock", + rpc_service="BedrockRuntime", + remote_service="AWS::BedrockRuntime", + remote_operation="InvokeModel", + remote_resource_type="AWS::Bedrock::Model", + remote_resource_identifier='cohere.command-light-text-v14', + cloudformation_primary_identifier="cohere.command-light-text-v14", + request_specific_attributes={ + _GEN_AI_REQUEST_MODEL: 'cohere.command-light-text-v14', + _GEN_AI_REQUEST_MAX_TOKENS: 512, + _GEN_AI_REQUEST_TEMPERATURE: 0.5, + _GEN_AI_REQUEST_TOP_P: 0.65 + }, + response_specific_attributes={ + _GEN_AI_RESPONSE_FINISH_REASONS: ['COMPLETE'], + _GEN_AI_USAGE_INPUT_TOKENS: math.ceil(len("Describe the purpose of a 'hello world' program in one line.") / 6), + _GEN_AI_USAGE_OUTPUT_TOKENS: math.ceil(len("test-generation-text") / 6) + }, + span_name="BedrockRuntime.InvokeModel" + ) + + def test_bedrock_runtime_invoke_model_ai21_jamba(self): + self.do_test_requests( + "bedrock/invokemodel/invoke-model/ai21.jamba-1-5-large-v1:0", + "GET", + 200, + 0, + 0, + local_operation="GET /bedrock", + rpc_service="BedrockRuntime", + remote_service="AWS::BedrockRuntime", + remote_operation="InvokeModel", + remote_resource_type="AWS::Bedrock::Model", + remote_resource_identifier='ai21.jamba-1-5-large-v1:0', + cloudformation_primary_identifier="ai21.jamba-1-5-large-v1:0", + request_specific_attributes={ + _GEN_AI_REQUEST_MODEL: 'ai21.jamba-1-5-large-v1:0', + _GEN_AI_REQUEST_MAX_TOKENS: 512, + _GEN_AI_REQUEST_TEMPERATURE: 0.6, + _GEN_AI_REQUEST_TOP_P: 0.8 + }, + response_specific_attributes={ + _GEN_AI_RESPONSE_FINISH_REASONS: ['stop'], + _GEN_AI_USAGE_INPUT_TOKENS: 21, + _GEN_AI_USAGE_OUTPUT_TOKENS: 24 + }, + span_name="BedrockRuntime.InvokeModel" + ) + + def test_bedrock_runtime_invoke_model_mistral_mistral(self): + self.do_test_requests( + "bedrock/invokemodel/invoke-model/mistral.mistral-7b-instruct-v0:2", + "GET", + 200, + 0, + 0, + local_operation="GET /bedrock", + rpc_service="BedrockRuntime", + remote_service="AWS::BedrockRuntime", + remote_operation="InvokeModel", + remote_resource_type="AWS::Bedrock::Model", + remote_resource_identifier='mistral.mistral-7b-instruct-v0:2', + cloudformation_primary_identifier="mistral.mistral-7b-instruct-v0:2", + request_specific_attributes={ + _GEN_AI_REQUEST_MODEL: 'mistral.mistral-7b-instruct-v0:2', + _GEN_AI_REQUEST_MAX_TOKENS: 4096, + _GEN_AI_REQUEST_TEMPERATURE: 0.75, + _GEN_AI_REQUEST_TOP_P: 0.99 + }, + response_specific_attributes={ + _GEN_AI_RESPONSE_FINISH_REASONS: ['stop'], + _GEN_AI_USAGE_INPUT_TOKENS: math.ceil(len("Describe the purpose of a 'hello world' program in one line.") / 6), + _GEN_AI_USAGE_OUTPUT_TOKENS: math.ceil(len("test-output-text") / 6) + }, + span_name="BedrockRuntime.InvokeModel" + ) + def test_bedrock_get_guardrail(self): self.do_test_requests( "bedrock/getguardrail/get-guardrail", @@ -438,8 +678,10 @@ def test_bedrock_get_guardrail(self): remote_operation="GetGuardrail", remote_resource_type="AWS::Bedrock::Guardrail", remote_resource_identifier="bt4o77i015cu", + cloudformation_primary_identifier="arn:aws:bedrock:us-east-1:000000000000:guardrail/bt4o77i015cu", request_specific_attributes={ _AWS_BEDROCK_GUARDRAIL_ID: "bt4o77i015cu", + _AWS_BEDROCK_GUARDRAIL_ARN: "arn:aws:bedrock:us-east-1:000000000000:guardrail/bt4o77i015cu" }, span_name="Bedrock.GetGuardrail", ) @@ -457,6 +699,7 @@ def test_bedrock_agent_runtime_invoke_agent(self): remote_operation="InvokeAgent", remote_resource_type="AWS::Bedrock::Agent", remote_resource_identifier="Q08WFRPHVL", + cloudformation_primary_identifier="Q08WFRPHVL", request_specific_attributes={ _AWS_BEDROCK_AGENT_ID: "Q08WFRPHVL", }, @@ -476,6 +719,7 @@ def test_bedrock_agent_runtime_retrieve(self): remote_operation="Retrieve", remote_resource_type="AWS::Bedrock::KnowledgeBase", remote_resource_identifier="test-knowledge-base-id", + cloudformation_primary_identifier="test-knowledge-base-id", request_specific_attributes={ _AWS_BEDROCK_KNOWLEDGE_BASE_ID: "test-knowledge-base-id", }, @@ -495,6 +739,7 @@ def test_bedrock_agent_get_agent(self): remote_operation="GetAgent", remote_resource_type="AWS::Bedrock::Agent", remote_resource_identifier="TESTAGENTID", + cloudformation_primary_identifier="TESTAGENTID", request_specific_attributes={ _AWS_BEDROCK_AGENT_ID: "TESTAGENTID", }, @@ -514,6 +759,7 @@ def test_bedrock_agent_get_knowledge_base(self): remote_operation="GetKnowledgeBase", remote_resource_type="AWS::Bedrock::KnowledgeBase", remote_resource_identifier="invalid-knowledge-base-id", + cloudformation_primary_identifier="invalid-knowledge-base-id", request_specific_attributes={ _AWS_BEDROCK_KNOWLEDGE_BASE_ID: "invalid-knowledge-base-id", }, @@ -533,12 +779,263 @@ def test_bedrock_agent_get_data_source(self): remote_operation="GetDataSource", remote_resource_type="AWS::Bedrock::DataSource", remote_resource_identifier="DATASURCID", + cloudformation_primary_identifier=r'TESTKBSEID\|DATASURCID', request_specific_attributes={ _AWS_BEDROCK_DATA_SOURCE_ID: "DATASURCID", }, span_name="BedrockAgent.GetDataSource", ) + def test_secretsmanager_fault(self): + self.do_test_requests( + "secretsmanager/fault", + "GET", + 500, + 0, + 1, + dp_count=3, + local_operation="GET /secretsmanager", + local_operation_2="POST /", + rpc_service="SecretsManager", + remote_service="AWS::SecretsManager", + remote_operation="DescribeSecret", + remote_resource_type="AWS::SecretsManager::Secret", + remote_resource_identifier="nonExistentSecret", + cloudformation_primary_identifier="arn:aws:secretsmanager:us-west-2:000000000000:secret:nonExistentSecret", + request_specific_attributes= { + _AWS_SECRET_ARN: "arn:aws:secretsmanager:us-west-2:000000000000:secret:nonExistentSecret", + }, + span_name="SecretsManager.DescribeSecret", + ) + + def test_secretsmanager_error(self): + self.do_test_requests( + "secretsmanager/error", + "GET", + 400, + 1, + 0, + local_operation="GET /secretsmanager", + rpc_service="SecretsManager", + remote_service="AWS::SecretsManager", + remote_operation="DescribeSecret", + remote_resource_type="AWS::SecretsManager::Secret", + remote_resource_identifier="nonExistentSecret", + cloudformation_primary_identifier="arn:aws:secretsmanager:us-west-2:000000000000:secret:nonExistentSecret", + request_specific_attributes= { + _AWS_SECRET_ARN: "arn:aws:secretsmanager:us-west-2:000000000000:secret:nonExistentSecret", + }, + span_name="SecretsManager.DescribeSecret", + ) + + def test_secretsmanager_describe_secret(self): + self.do_test_requests( + "secretsmanager/describesecret/my-secret", + "GET", + 200, + 0, + 0, + local_operation="GET /secretsmanager", + rpc_service="SecretsManager", + remote_service="AWS::SecretsManager", + remote_operation="DescribeSecret", + remote_resource_type="AWS::SecretsManager::Secret", + remote_resource_identifier=r'MyTestSecret-[a-zA-Z0-9]{6}$', + cloudformation_primary_identifier=r'arn:aws:secretsmanager:us-west-2:000000000000:secret:MyTestSecret-[a-zA-Z0-9]{6}$', + response_specific_attributes= { + _AWS_SECRET_ARN: r'arn:aws:secretsmanager:us-west-2:000000000000:secret:MyTestSecret-[a-zA-Z0-9]{6}$', + }, + span_name="SecretsManager.DescribeSecret", + ) + + def test_stepfunctions_fault(self): + self.do_test_requests( + "stepfunctions/fault", + "GET", + 500, + 0, + 1, + dp_count=3, + local_operation="GET /stepfunctions", + local_operation_2="POST /", + rpc_service="SFN", + remote_service="AWS::StepFunctions", + remote_operation="DescribeStateMachine", + remote_resource_type="AWS::StepFunctions::StateMachine", + remote_resource_identifier="invalid-state-machine", + cloudformation_primary_identifier="arn:aws:states:us-west-2:000000000000:stateMachine:invalid-state-machine", + request_specific_attributes= { + _AWS_STATE_MACHINE_ARN: "arn:aws:states:us-west-2:000000000000:stateMachine:invalid-state-machine", + }, + span_name="SFN.DescribeStateMachine", + ) + + def test_stepfunctions_error(self): + self.do_test_requests( + "stepfunctions/error", + "GET", + 400, + 1, + 0, + local_operation="GET /stepfunctions", + rpc_service="SFN", + remote_service="AWS::StepFunctions", + remote_operation="DescribeStateMachine", + remote_resource_type="AWS::StepFunctions::StateMachine", + remote_resource_identifier="nonExistentStateMachine", + cloudformation_primary_identifier="arn:aws:states:us-west-2:000000000000:stateMachine:nonExistentStateMachine", + request_specific_attributes= { + _AWS_STATE_MACHINE_ARN: "arn:aws:states:us-west-2:000000000000:stateMachine:nonExistentStateMachine", + }, + span_name="SFN.DescribeStateMachine", + ) + + def test_stepfunctions_describe_state_machine(self): + self.do_test_requests( + "stepfunctions/describestatemachine/state-machine", + "GET", + 200, + 0, + 0, + local_operation="GET /stepfunctions", + rpc_service="SFN", + remote_service="AWS::StepFunctions", + remote_operation="DescribeStateMachine", + remote_resource_type="AWS::StepFunctions::StateMachine", + remote_resource_identifier="TestStateMachine", + cloudformation_primary_identifier="arn:aws:states:us-west-2:000000000000:stateMachine:TestStateMachine", + request_specific_attributes= { + _AWS_STATE_MACHINE_ARN: "arn:aws:states:us-west-2:000000000000:stateMachine:TestStateMachine", + }, + span_name="SFN.DescribeStateMachine", + ) + + def test_stepfunctions_describe_activity(self): + self.do_test_requests( + "stepfunctions/describeactivity/activity", + "GET", + 200, + 0, + 0, + local_operation="GET /stepfunctions", + rpc_service="SFN", + remote_service="AWS::StepFunctions", + remote_operation="DescribeActivity", + remote_resource_type="AWS::StepFunctions::Activity", + remote_resource_identifier="TestActivity", + cloudformation_primary_identifier="arn:aws:states:us-west-2:000000000000:activity:TestActivity", + request_specific_attributes= { + _AWS_ACTIVITY_ARN: "arn:aws:states:us-west-2:000000000000:activity:TestActivity", + }, + span_name="SFN.DescribeActivity", + ) + + def test_sns_fault(self): + self.do_test_requests( + "sns/fault", + "GET", + 500, + 0, + 1, + dp_count=3, + local_operation="GET /sns", + local_operation_2="POST /", + rpc_service="SNS", + remote_service="AWS::SNS", + remote_operation="GetTopicAttributes", + remote_resource_type="AWS::SNS::Topic", + remote_resource_identifier="invalidTopic", + cloudformation_primary_identifier="arn:aws:sns:us-west-2:000000000000:invalidTopic", + request_specific_attributes= { + _AWS_SNS_TOPIC_ARN: "arn:aws:sns:us-west-2:000000000000:invalidTopic", + }, + span_name="SNS GetTopicAttributes", + ) + + def test_sns_error(self): + self.do_test_requests( + "sns/error", + "GET", + 404, # this is the expected status code error for sns + 1, + 0, + local_operation="GET /sns", + rpc_service="SNS", + remote_service="AWS::SNS", + remote_operation="GetTopicAttributes", + remote_resource_type="AWS::SNS::Topic", + remote_resource_identifier="nonExistentTopic", + cloudformation_primary_identifier="arn:aws:sns:us-west-2:000000000000:nonExistentTopic", + request_specific_attributes= { + _AWS_SNS_TOPIC_ARN: "arn:aws:sns:us-west-2:000000000000:nonExistentTopic", + }, + span_name="SNS GetTopicAttributes", + ) + + def test_sns_get_topic_attributes(self): + self.do_test_requests( + "sns/gettopicattributes/topic", + "GET", + 200, + 0, + 0, + local_operation="GET /sns", + rpc_service="SNS", + remote_service="AWS::SNS", + remote_operation="GetTopicAttributes", + remote_resource_type="AWS::SNS::Topic", + remote_resource_identifier="TestTopic", + cloudformation_primary_identifier="arn:aws:sns:us-west-2:000000000000:TestTopic", + request_specific_attributes= { + _AWS_SNS_TOPIC_ARN: "arn:aws:sns:us-west-2:000000000000:TestTopic", + }, + span_name="SNS GetTopicAttributes", + ) + + def test_lambda_fault(self): + self.do_test_requests( + "lambda/fault", + "GET", + 500, + 0, + 1, + dp_count=3, + local_operation="GET /lambda", + local_operation_2="PUT /2015-03-31", + rpc_service="Lambda", + remote_service="AWS::Lambda", + remote_operation="UpdateEventSourceMapping", + remote_resource_type="AWS::Lambda::EventSourceMapping", + remote_resource_identifier="123e4567-e89b-12d3-a456-426614174000", + cloudformation_primary_identifier="123e4567-e89b-12d3-a456-426614174000", + request_specific_attributes= { + _AWS_LAMBDA_RESOURCE_MAPPING_ID: "123e4567-e89b-12d3-a456-426614174000", + }, + span_name="Lambda.UpdateEventSourceMapping", + ) + + def test_lambda_error(self): + self.do_test_requests( + "lambda/error", + "GET", + 404, + 1, + 0, + local_operation="GET /lambda", + rpc_service="Lambda", + remote_service="AWS::Lambda", + remote_operation="GetEventSourceMapping", + remote_resource_type="AWS::Lambda::EventSourceMapping", + remote_resource_identifier="nonExistentUUID", + cloudformation_primary_identifier="nonExistentUUID", + request_specific_attributes= { + _AWS_LAMBDA_RESOURCE_MAPPING_ID: "nonExistentUUID", + }, + span_name="Lambda.GetEventSourceMapping", + ) + + #TODO: Need to add test_lambda_get_event_source_mapping once workaround is figured out for storing UUID between tests + @override def _assert_aws_span_attributes(self, resource_scope_spans: List[ResourceScopeSpan], path: str, **kwargs) -> None: target_spans: List[Span] = [] @@ -564,6 +1061,7 @@ def _assert_aws_span_attributes(self, resource_scope_spans: List[ResourceScopeSp span_kind, kwargs.get("remote_resource_type", "None"), kwargs.get("remote_resource_identifier", "None"), + kwargs.get("cloudformation_primary_identifier", "None"), ) def _assert_aws_attributes( @@ -575,6 +1073,7 @@ def _assert_aws_attributes( span_kind: str, remote_resource_type: str, remote_resource_identifier: str, + cloudformation_primary_identifier: str ) -> None: attributes_dict: Dict[str, AnyValue] = self._get_attributes_dict(attributes_list) self._assert_str_attribute(attributes_dict, AWS_LOCAL_SERVICE, self.get_application_otel_service_name()) @@ -582,15 +1081,19 @@ def _assert_aws_attributes( self._assert_str_attribute(attributes_dict, AWS_REMOTE_SERVICE, remote_service) self._assert_str_attribute(attributes_dict, AWS_REMOTE_OPERATION, remote_operation) if remote_resource_type != "None": - self._assert_str_attribute(attributes_dict, AWS_REMOTE_RESOURCE_TYPE, remote_resource_type) + self._assert_attribute(attributes_dict, AWS_REMOTE_RESOURCE_TYPE, remote_resource_type) if remote_resource_identifier != "None": - self._assert_str_attribute(attributes_dict, AWS_REMOTE_RESOURCE_IDENTIFIER, remote_resource_identifier) + self._assert_attribute(attributes_dict, AWS_REMOTE_RESOURCE_IDENTIFIER, remote_resource_identifier) + if cloudformation_primary_identifier != "None": + self._assert_attribute(attributes_dict, AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER, cloudformation_primary_identifier) + self._assert_str_attribute(attributes_dict, AWS_SPAN_KIND, span_kind) @override def _assert_semantic_conventions_span_attributes( self, resource_scope_spans: List[ResourceScopeSpan], method: str, path: str, status_code: int, **kwargs ) -> None: + target_spans: List[Span] = [] for resource_scope_span in resource_scope_spans: # pylint: disable=no-member @@ -606,7 +1109,8 @@ def _assert_semantic_conventions_span_attributes( kwargs.get("remote_operation"), status_code, kwargs.get("request_specific_attributes", {}), - ) + kwargs.get("response_specific_attributes", {}), + ) # pylint: disable=unidiomatic-typecheck def _assert_semantic_conventions_attributes( @@ -616,6 +1120,7 @@ def _assert_semantic_conventions_attributes( operation: str, status_code: int, request_specific_attributes: dict, + response_specific_attributes: dict, ) -> None: attributes_dict: Dict[str, AnyValue] = self._get_attributes_dict(attributes_list) self._assert_str_attribute(attributes_dict, SpanAttributes.RPC_METHOD, operation) @@ -625,12 +1130,29 @@ def _assert_semantic_conventions_attributes( # TODO: aws sdk instrumentation is not respecting PEER_SERVICE # self._assert_str_attribute(attributes_dict, SpanAttributes.PEER_SERVICE, "backend:8080") for key, value in request_specific_attributes.items(): - if isinstance(value, str): - self._assert_str_attribute(attributes_dict, key, value) - elif isinstance(value, int): - self._assert_int_attribute(attributes_dict, key, value) - else: - self._assert_array_value_ddb_table_name(attributes_dict, key, value) + self._assert_attribute(attributes_dict, key, value) + + for key, value in response_specific_attributes.items(): + self._assert_attribute(attributes_dict, key, value) + + def _assert_attribute(self, attributes_dict: Dict[str, AnyValue], key, value) -> None: + if isinstance(value, str): + self._assert_str_attribute(attributes_dict, key, value) + elif isinstance(value, int): + self._assert_int_attribute(attributes_dict, key, value) + elif isinstance(value, float): + self._assert_float_attribute(attributes_dict, key, value) + else: + self._assert_array_value_ddb_table_name(attributes_dict, key, value) + + @override + def _assert_str_attribute(self, attributes_dict: Dict[str, AnyValue], key: str, expected_value: str): + self.assertIn(key, attributes_dict) + actual_value: AnyValue = attributes_dict[key] + self.assertIsNotNone(actual_value) + pattern = re.compile(expected_value) + match = pattern.fullmatch(actual_value.string_value) + self.assertTrue(match is not None, f"Actual: {actual_value.string_value} does not match Expected: {expected_value}") @override def _assert_metric_attributes( @@ -658,23 +1180,23 @@ def _assert_metric_attributes( dependency_dp = dp_list[1] service_dp = dp_list[0] attribute_dict: Dict[str, AnyValue] = self._get_attributes_dict(dependency_dp.attributes) - self._assert_str_attribute(attribute_dict, AWS_LOCAL_SERVICE, self.get_application_otel_service_name()) - self._assert_str_attribute(attribute_dict, AWS_LOCAL_OPERATION, kwargs.get("local_operation")) - self._assert_str_attribute(attribute_dict, AWS_REMOTE_SERVICE, kwargs.get("remote_service")) - self._assert_str_attribute(attribute_dict, AWS_REMOTE_OPERATION, kwargs.get("remote_operation")) - self._assert_str_attribute(attribute_dict, AWS_SPAN_KIND, kwargs.get("dependency_metric_span_kind") or "CLIENT") + self._assert_attribute(attribute_dict, AWS_LOCAL_SERVICE, self.get_application_otel_service_name()) + self._assert_attribute(attribute_dict, AWS_LOCAL_OPERATION, kwargs.get("local_operation")) + self._assert_attribute(attribute_dict, AWS_REMOTE_SERVICE, kwargs.get("remote_service")) + self._assert_attribute(attribute_dict, AWS_REMOTE_OPERATION, kwargs.get("remote_operation")) + self._assert_attribute(attribute_dict, AWS_SPAN_KIND, kwargs.get("dependency_metric_span_kind") or "CLIENT") remote_resource_type = kwargs.get("remote_resource_type", "None") remote_resource_identifier = kwargs.get("remote_resource_identifier", "None") if remote_resource_type != "None": - self._assert_str_attribute(attribute_dict, AWS_REMOTE_RESOURCE_TYPE, remote_resource_type) + self._assert_attribute(attribute_dict, AWS_REMOTE_RESOURCE_TYPE, remote_resource_type) if remote_resource_identifier != "None": - self._assert_str_attribute(attribute_dict, AWS_REMOTE_RESOURCE_IDENTIFIER, remote_resource_identifier) + self._assert_attribute(attribute_dict, AWS_REMOTE_RESOURCE_IDENTIFIER, remote_resource_identifier) self.check_sum(metric_name, dependency_dp.sum, expected_sum) attribute_dict: Dict[str, AnyValue] = self._get_attributes_dict(service_dp.attributes) - self._assert_str_attribute(attribute_dict, AWS_LOCAL_SERVICE, self.get_application_otel_service_name()) - self._assert_str_attribute(attribute_dict, AWS_LOCAL_OPERATION, kwargs.get("local_operation")) - self._assert_str_attribute(attribute_dict, AWS_SPAN_KIND, "LOCAL_ROOT") + self._assert_attribute(attribute_dict, AWS_LOCAL_SERVICE, self.get_application_otel_service_name()) + self._assert_attribute(attribute_dict, AWS_LOCAL_OPERATION, kwargs.get("local_operation")) + self._assert_attribute(attribute_dict, AWS_SPAN_KIND, "LOCAL_ROOT") self.check_sum(metric_name, service_dp.sum, expected_sum) else: dependency_dp: ExponentialHistogramDataPoint = max(dp_list, key=lambda dp: len(dp.attributes)) @@ -691,9 +1213,9 @@ def _assert_metric_attributes( remote_resource_type = kwargs.get("remote_resource_type", "None") remote_resource_identifier = kwargs.get("remote_resource_identifier", "None") if remote_resource_type != "None": - self._assert_str_attribute(attribute_dict, AWS_REMOTE_RESOURCE_TYPE, remote_resource_type) + self._assert_attribute(attribute_dict, AWS_REMOTE_RESOURCE_TYPE, remote_resource_type) if remote_resource_identifier != "None": - self._assert_str_attribute(attribute_dict, AWS_REMOTE_RESOURCE_IDENTIFIER, remote_resource_identifier) + self._assert_attribute(attribute_dict, AWS_REMOTE_RESOURCE_IDENTIFIER, remote_resource_identifier) self.check_sum(metric_name, dependency_dp.sum, expected_sum) attribute_dict_service: Dict[str, AnyValue] = self._get_attributes_dict(service_dp.attributes) @@ -715,10 +1237,29 @@ def _assert_metric_attributes( self._assert_str_attribute(attribute_dict_other, AWS_SPAN_KIND, "LOCAL_ROOT") self.check_sum(metric_name, other_dp.sum, expected_sum) + def _assert_attribute(self, attributes_dict: Dict[str, AnyValue], key, value) -> None: + if isinstance(value, str): + self._assert_str_attribute(attributes_dict, key, value) + elif isinstance(value, int): + self._assert_int_attribute(attributes_dict, key, value) + elif isinstance(value, float): + self._assert_float_attribute(attributes_dict, key, value) + else: + self._assert_array_value_ddb_table_name(attributes_dict, key, value) + + @override + def _assert_str_attribute(self, attributes_dict: Dict[str, AnyValue], key: str, expected_value: str): + self.assertIn(key, attributes_dict) + actual_value: AnyValue = attributes_dict[key] + self.assertIsNotNone(actual_value) + pattern = re.compile(expected_value) + match = pattern.fullmatch(actual_value.string_value) + self.assertTrue(match is not None, f"Actual: {actual_value.string_value} does not match Expected: {expected_value}") + # pylint: disable=consider-using-enumerate def _assert_array_value_ddb_table_name(self, attributes_dict: Dict[str, AnyValue], key: str, expect_values: list): self.assertIn(key, attributes_dict) actual_values: [AnyValue] = attributes_dict[key].array_value self.assertEqual(len(actual_values.values), len(expect_values)) for index in range(len(actual_values.values)): - self.assertEqual(actual_values.values[index].string_value, expect_values[index]) + self.assertEqual(actual_values.values[index].string_value, expect_values[index]) \ No newline at end of file diff --git a/contract-tests/tests/test/amazon/base/contract_test_base.py b/contract-tests/tests/test/amazon/base/contract_test_base.py index af6557c..07a59ab 100644 --- a/contract-tests/tests/test/amazon/base/contract_test_base.py +++ b/contract-tests/tests/test/amazon/base/contract_test_base.py @@ -1,6 +1,7 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 import time +import re from logging import INFO, Logger, getLogger from typing import Dict, List from unittest import TestCase @@ -229,6 +230,12 @@ def _assert_int_attribute(self, attributes_dict: Dict[str, AnyValue], key: str, actual_value: AnyValue = attributes_dict[key] self.assertIsNotNone(actual_value) self.assertEqual(expected_value, actual_value.int_value) + + def _assert_float_attribute(self, attributes_dict: Dict[str, AnyValue], key: str, expected_value: float) -> None: + self.assertIn(key, attributes_dict) + actual_value: AnyValue = attributes_dict[key] + self.assertIsNotNone(actual_value) + self.assertEqual(expected_value, actual_value.double_value) def check_sum(self, metric_name: str, actual_sum: float, expected_sum: float) -> None: if metric_name is LATENCY_METRIC: @@ -279,4 +286,4 @@ def _assert_semantic_conventions_span_attributes( def _assert_metric_attributes( self, resource_scope_metrics: List[ResourceScopeMetric], metric_name: str, expected_sum: int, **kwargs ): - self.fail("Tests must implement this function") + self.fail("Tests must implement this function") \ No newline at end of file diff --git a/contract-tests/tests/test/amazon/utils/application_signals_constants.py b/contract-tests/tests/test/amazon/utils/application_signals_constants.py index 9f3a625..14b602e 100644 --- a/contract-tests/tests/test/amazon/utils/application_signals_constants.py +++ b/contract-tests/tests/test/amazon/utils/application_signals_constants.py @@ -17,4 +17,5 @@ AWS_REMOTE_OPERATION: str = "aws.remote.operation" AWS_REMOTE_RESOURCE_TYPE: str = "aws.remote.resource.type" AWS_REMOTE_RESOURCE_IDENTIFIER: str = "aws.remote.resource.identifier" +AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER: str = 'aws.remote.resource.cfn.primary.identifier' AWS_SPAN_KIND: str = "aws.span.kind" diff --git a/docker-utils/package.json b/docker-utils/package.json index 572038e..716bff3 100644 --- a/docker-utils/package.json +++ b/docker-utils/package.json @@ -1,6 +1,6 @@ { "name": "@aws/aws-distro-opentelemetry-node-autoinstrumentation", - "version": "0.3.0-dev0", + "version": "0.4.0-dev0", "private": true, "scripts": { "clean": "rimraf build/*", diff --git a/lambda-layer/README.md b/lambda-layer/README.md index b82c9f4..5dc9c91 100644 --- a/lambda-layer/README.md +++ b/lambda-layer/README.md @@ -1,5 +1,7 @@ # AWS Distro Lambda Layer for NodeJS +This folder provides support for **Application Signals** in AWS Lambda environments. You can explore this repository to learn how to build a Lambda layer for AWS Node.js Runtimes from scratch in your AWS account. Alternatively, you can directly visit the AWS documentation, [Enable Application Signals on Lambda functions](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Application-Signals-Enable-Lambda.html), and use the AWS-managed Lambda layers we provide. + By default, the layer enables aws-sdk and aws-lambda instrumentation libraries only for better Lambda cold start performance. You can use the environment variable `OTEL_NODE_ENABLED_INSTRUMENTATIONS` to enable only certain diff --git a/lambda-layer/packages/layer/package.json b/lambda-layer/packages/layer/package.json index 04d048b..cd729c6 100644 --- a/lambda-layer/packages/layer/package.json +++ b/lambda-layer/packages/layer/package.json @@ -33,6 +33,6 @@ "instrumentation" ], "dependencies": { - "@aws/aws-distro-opentelemetry-node-autoinstrumentation": "file:../../../aws-distro-opentelemetry-node-autoinstrumentation/aws-aws-distro-opentelemetry-node-autoinstrumentation-0.3.0-dev0.tgz" + "@aws/aws-distro-opentelemetry-node-autoinstrumentation": "file:../../../aws-distro-opentelemetry-node-autoinstrumentation/aws-aws-distro-opentelemetry-node-autoinstrumentation-0.4.0-dev0.tgz" } } diff --git a/package-lock.json b/package-lock.json index 768355f..7d8f801 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "aws-otel-js-instrumentation", - "version": "0.3.0-dev0", + "version": "0.4.0-dev0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "aws-otel-js-instrumentation", - "version": "0.3.0-dev0", + "version": "0.4.0-dev0", "license": "Apache-2.0", "workspaces": [ "aws-distro-opentelemetry-node-autoinstrumentation/" @@ -34,7 +34,7 @@ }, "aws-distro-opentelemetry-node-autoinstrumentation": { "name": "@aws/aws-distro-opentelemetry-node-autoinstrumentation", - "version": "0.3.0-dev0", + "version": "0.4.0-dev0", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "1.9.0", @@ -62,8 +62,11 @@ "@aws-sdk/client-bedrock-agent-runtime": "3.632.0", "@aws-sdk/client-bedrock-runtime": "3.632.0", "@aws-sdk/client-kinesis": "3.632.0", + "@aws-sdk/client-lambda": "^3.632.0", "@aws-sdk/client-s3": "3.632.0", - "@aws-sdk/client-sqs": "3.632.0", + "@aws-sdk/client-secrets-manager": "^3.632.0", + "@aws-sdk/client-sfn": "^3.632.0", + "@aws-sdk/client-sns": "^3.632.0", "@opentelemetry/contrib-test-utils": "0.41.0", "@types/mocha": "7.0.2", "@types/node": "18.6.5", @@ -614,6 +617,577 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/client-lambda": { + "version": "3.699.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.699.0.tgz", + "integrity": "sha512-K9TGvQB8hkjwNhfWSfYllUpttqxTcd78ShSRCIhlcwzzsmQphET10xEb0Tm1k8sqriSQ+CiVOFSkX78gqoHzBg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.699.0", + "@aws-sdk/client-sts": "3.699.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/credential-provider-node": "3.699.0", + "@aws-sdk/middleware-host-header": "3.696.0", + "@aws-sdk/middleware-logger": "3.696.0", + "@aws-sdk/middleware-recursion-detection": "3.696.0", + "@aws-sdk/middleware-user-agent": "3.696.0", + "@aws-sdk/region-config-resolver": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@aws-sdk/util-endpoints": "3.696.0", + "@aws-sdk/util-user-agent-browser": "3.696.0", + "@aws-sdk/util-user-agent-node": "3.696.0", + "@smithy/config-resolver": "^3.0.12", + "@smithy/core": "^2.5.3", + "@smithy/eventstream-serde-browser": "^3.0.13", + "@smithy/eventstream-serde-config-resolver": "^3.0.10", + "@smithy/eventstream-serde-node": "^3.0.12", + "@smithy/fetch-http-handler": "^4.1.1", + "@smithy/hash-node": "^3.0.10", + "@smithy/invalid-dependency": "^3.0.10", + "@smithy/middleware-content-length": "^3.0.12", + "@smithy/middleware-endpoint": "^3.2.3", + "@smithy/middleware-retry": "^3.0.27", + "@smithy/middleware-serde": "^3.0.10", + "@smithy/middleware-stack": "^3.0.10", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/node-http-handler": "^3.3.1", + "@smithy/protocol-http": "^4.1.7", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "@smithy/url-parser": "^3.0.10", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.27", + "@smithy/util-defaults-mode-node": "^3.0.27", + "@smithy/util-endpoints": "^2.1.6", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-retry": "^3.0.10", + "@smithy/util-stream": "^3.3.1", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/client-sso": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.696.0.tgz", + "integrity": "sha512-q5TTkd08JS0DOkHfUL853tuArf7NrPeqoS5UOvqJho8ibV9Ak/a/HO4kNvy9Nj3cib/toHYHsQIEtecUPSUUrQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/middleware-host-header": "3.696.0", + "@aws-sdk/middleware-logger": "3.696.0", + "@aws-sdk/middleware-recursion-detection": "3.696.0", + "@aws-sdk/middleware-user-agent": "3.696.0", + "@aws-sdk/region-config-resolver": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@aws-sdk/util-endpoints": "3.696.0", + "@aws-sdk/util-user-agent-browser": "3.696.0", + "@aws-sdk/util-user-agent-node": "3.696.0", + "@smithy/config-resolver": "^3.0.12", + "@smithy/core": "^2.5.3", + "@smithy/fetch-http-handler": "^4.1.1", + "@smithy/hash-node": "^3.0.10", + "@smithy/invalid-dependency": "^3.0.10", + "@smithy/middleware-content-length": "^3.0.12", + "@smithy/middleware-endpoint": "^3.2.3", + "@smithy/middleware-retry": "^3.0.27", + "@smithy/middleware-serde": "^3.0.10", + "@smithy/middleware-stack": "^3.0.10", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/node-http-handler": "^3.3.1", + "@smithy/protocol-http": "^4.1.7", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "@smithy/url-parser": "^3.0.10", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.27", + "@smithy/util-defaults-mode-node": "^3.0.27", + "@smithy/util-endpoints": "^2.1.6", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-retry": "^3.0.10", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.699.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.699.0.tgz", + "integrity": "sha512-u8a1GorY5D1l+4FQAf4XBUC1T10/t7neuwT21r0ymrtMFSK2a9QqVHKMoLkvavAwyhJnARSBM9/UQC797PFOFw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/credential-provider-node": "3.699.0", + "@aws-sdk/middleware-host-header": "3.696.0", + "@aws-sdk/middleware-logger": "3.696.0", + "@aws-sdk/middleware-recursion-detection": "3.696.0", + "@aws-sdk/middleware-user-agent": "3.696.0", + "@aws-sdk/region-config-resolver": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@aws-sdk/util-endpoints": "3.696.0", + "@aws-sdk/util-user-agent-browser": "3.696.0", + "@aws-sdk/util-user-agent-node": "3.696.0", + "@smithy/config-resolver": "^3.0.12", + "@smithy/core": "^2.5.3", + "@smithy/fetch-http-handler": "^4.1.1", + "@smithy/hash-node": "^3.0.10", + "@smithy/invalid-dependency": "^3.0.10", + "@smithy/middleware-content-length": "^3.0.12", + "@smithy/middleware-endpoint": "^3.2.3", + "@smithy/middleware-retry": "^3.0.27", + "@smithy/middleware-serde": "^3.0.10", + "@smithy/middleware-stack": "^3.0.10", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/node-http-handler": "^3.3.1", + "@smithy/protocol-http": "^4.1.7", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "@smithy/url-parser": "^3.0.10", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.27", + "@smithy/util-defaults-mode-node": "^3.0.27", + "@smithy/util-endpoints": "^2.1.6", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-retry": "^3.0.10", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.699.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/client-sts": { + "version": "3.699.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.699.0.tgz", + "integrity": "sha512-++lsn4x2YXsZPIzFVwv3fSUVM55ZT0WRFmPeNilYIhZClxHLmVAWKH4I55cY9ry60/aTKYjzOXkWwyBKGsGvQg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.699.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/credential-provider-node": "3.699.0", + "@aws-sdk/middleware-host-header": "3.696.0", + "@aws-sdk/middleware-logger": "3.696.0", + "@aws-sdk/middleware-recursion-detection": "3.696.0", + "@aws-sdk/middleware-user-agent": "3.696.0", + "@aws-sdk/region-config-resolver": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@aws-sdk/util-endpoints": "3.696.0", + "@aws-sdk/util-user-agent-browser": "3.696.0", + "@aws-sdk/util-user-agent-node": "3.696.0", + "@smithy/config-resolver": "^3.0.12", + "@smithy/core": "^2.5.3", + "@smithy/fetch-http-handler": "^4.1.1", + "@smithy/hash-node": "^3.0.10", + "@smithy/invalid-dependency": "^3.0.10", + "@smithy/middleware-content-length": "^3.0.12", + "@smithy/middleware-endpoint": "^3.2.3", + "@smithy/middleware-retry": "^3.0.27", + "@smithy/middleware-serde": "^3.0.10", + "@smithy/middleware-stack": "^3.0.10", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/node-http-handler": "^3.3.1", + "@smithy/protocol-http": "^4.1.7", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "@smithy/url-parser": "^3.0.10", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.27", + "@smithy/util-defaults-mode-node": "^3.0.27", + "@smithy/util-endpoints": "^2.1.6", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-retry": "^3.0.10", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/core": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.696.0.tgz", + "integrity": "sha512-3c9III1k03DgvRZWg8vhVmfIXPG6hAciN9MzQTzqGngzWAELZF/WONRTRQuDFixVtarQatmLHYVw/atGeA2Byw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.696.0", + "@smithy/core": "^2.5.3", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.7", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "@smithy/util-middleware": "^3.0.10", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.696.0.tgz", + "integrity": "sha512-T9iMFnJL7YTlESLpVFT3fg1Lkb1lD+oiaIC8KMpepb01gDUBIpj9+Y+pA/cgRWW0yRxmkDXNazAE2qQTVFGJzA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.696.0.tgz", + "integrity": "sha512-GV6EbvPi2eq1+WgY/o2RFA3P7HGmnkIzCNmhwtALFlqMroLYWKE7PSeHw66Uh1dFQeVESn0/+hiUNhu1mB0emA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/fetch-http-handler": "^4.1.1", + "@smithy/node-http-handler": "^3.3.1", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.7", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "@smithy/util-stream": "^3.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.699.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.699.0.tgz", + "integrity": "sha512-dXmCqjJnKmG37Q+nLjPVu22mNkrGHY8hYoOt3Jo9R2zr5MYV7s/NHsCHr+7E+BZ+tfZYLRPeB1wkpTeHiEcdRw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.696.0", + "@aws-sdk/credential-provider-env": "3.696.0", + "@aws-sdk/credential-provider-http": "3.696.0", + "@aws-sdk/credential-provider-process": "3.696.0", + "@aws-sdk/credential-provider-sso": "3.699.0", + "@aws-sdk/credential-provider-web-identity": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.699.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.699.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.699.0.tgz", + "integrity": "sha512-MmEmNDo1bBtTgRmdNfdQksXu4uXe66s0p1hi1YPrn1h59Q605eq/xiWbGL6/3KdkViH6eGUuABeV2ODld86ylg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.696.0", + "@aws-sdk/credential-provider-http": "3.696.0", + "@aws-sdk/credential-provider-ini": "3.699.0", + "@aws-sdk/credential-provider-process": "3.696.0", + "@aws-sdk/credential-provider-sso": "3.699.0", + "@aws-sdk/credential-provider-web-identity": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.696.0.tgz", + "integrity": "sha512-mL1RcFDe9sfmyU5K1nuFkO8UiJXXxLX4JO1gVaDIOvPqwStpUAwi3A1BoeZhWZZNQsiKI810RnYGo0E0WB/hUA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.699.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.699.0.tgz", + "integrity": "sha512-Ekp2cZG4pl9D8+uKWm4qO1xcm8/MeiI8f+dnlZm8aQzizeC+aXYy9GyoclSf6daK8KfRPiRfM7ZHBBL5dAfdMA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.696.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/token-providers": "3.699.0", + "@aws-sdk/types": "3.696.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.696.0.tgz", + "integrity": "sha512-XJ/CVlWChM0VCoc259vWguFUjJDn/QwDqHwbx+K9cg3v6yrqXfK5ai+p/6lx0nQpnk4JzPVeYYxWRpaTsGC9rg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.696.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.696.0.tgz", + "integrity": "sha512-zELJp9Ta2zkX7ELggMN9qMCgekqZhFC5V2rOr4hJDEb/Tte7gpfKSObAnw/3AYiVqt36sjHKfdkoTsuwGdEoDg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.696.0", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-logger": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.696.0.tgz", + "integrity": "sha512-KhkHt+8AjCxcR/5Zp3++YPJPpFQzxpr+jmONiT/Jw2yqnSngZ0Yspm5wGoRx2hS1HJbyZNuaOWEGuJoxLeBKfA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.696.0", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.696.0.tgz", + "integrity": "sha512-si/maV3Z0hH7qa99f9ru2xpS5HlfSVcasRlNUXKSDm611i7jFMWwGNLUOXFAOLhXotPX5G3Z6BLwL34oDeBMug==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.696.0", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.696.0.tgz", + "integrity": "sha512-Lvyj8CTyxrHI6GHd2YVZKIRI5Fmnugt3cpJo0VrKKEgK5zMySwEZ1n4dqPK6czYRWKd5+WnYHYAuU+Wdk6Jsjw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@aws-sdk/util-endpoints": "3.696.0", + "@smithy/core": "^2.5.3", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.696.0.tgz", + "integrity": "sha512-7EuH142lBXjI8yH6dVS/CZeiK/WZsmb/8zP6bQbVYpMrppSTgB3MzZZdxVZGzL5r8zPQOU10wLC4kIMy0qdBVQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.696.0", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/types": "^3.7.1", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.10", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/token-providers": { + "version": "3.699.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.699.0.tgz", + "integrity": "sha512-kuiEW9DWs7fNos/SM+y58HCPhcIzm1nEZLhe2/7/6+TvAYLuEWURYsbK48gzsxXlaJ2k/jGY3nIsA7RptbMOwA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.696.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.699.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/types": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.696.0.tgz", + "integrity": "sha512-9rTvUJIAj5d3//U5FDPWGJ1nFJLuWb30vugGOrWk7aNZ6y9tuA3PI7Cc9dP8WEXKVyK1vuuk8rSFP2iqXnlgrw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/util-endpoints": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.696.0.tgz", + "integrity": "sha512-T5s0IlBVX+gkb9g/I6CLt4yAZVzMSiGnbUqWihWsHvQR1WOoIcndQy/Oz/IJXT9T2ipoy7a80gzV6a5mglrioA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.696.0", + "@smithy/types": "^3.7.1", + "@smithy/util-endpoints": "^2.1.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.696.0.tgz", + "integrity": "sha512-Z5rVNDdmPOe6ELoM5AhF/ja5tSjbe6ctSctDPb0JdDf4dT0v2MfwhJKzXju2RzX8Es/77Glh7MlaXLE0kCB9+Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.696.0", + "@smithy/types": "^3.7.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.696.0.tgz", + "integrity": "sha512-KhKqcfyXIB0SCCt+qsu4eJjsfiOrNzK5dCV7RAW2YIpp+msxGUUX0NdRE9rkzjiv+3EMktgJm3eEIS+yxtlVdQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.1.tgz", + "integrity": "sha512-bH7QW0+JdX0bPBadXt8GwMof/jz0H28I84hU1Uet9ISpzUqXqRQ3fEZJ+ANPOhzSEczYvANNl3uDQDYArSFDtA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.7", + "@smithy/querystring-builder": "^3.0.10", + "@smithy/types": "^3.7.1", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, "node_modules/@aws-sdk/client-s3": { "version": "3.632.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.632.0.tgz", @@ -684,10 +1258,118 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/client-sqs": { + "node_modules/@aws-sdk/client-secrets-manager": { "version": "3.632.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.632.0.tgz", - "integrity": "sha512-UK4JQ6nF6qDtc2rLdrYTgyZWTdUgfVHbVHQdHkrni2TNac3kEksgiDFxsm6zQjy1NoOg4YdPDeMOeHRvWImlPQ==", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.632.0.tgz", + "integrity": "sha512-WsQhPHHK1yPfALcP1B7nBSGDzky6vFTUEXnUdfzb5Xy2cT+JTBTS6ChtQGqqOuGHDP/3t/9soqZ+L6rUCYBb/Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.632.0", + "@aws-sdk/client-sts": "3.632.0", + "@aws-sdk/core": "3.629.0", + "@aws-sdk/credential-provider-node": "3.632.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.632.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.632.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.2", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.14", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.14", + "@smithy/util-defaults-mode-node": "^3.0.14", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn": { + "version": "3.632.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sfn/-/client-sfn-3.632.0.tgz", + "integrity": "sha512-LPaMqALuArGX1Y5N3tHX4lNlZabsqgfGJZjwyLTHawqiGNDMlMqOUg4Dl0e2Ies8yvMjs//YXtMWz26x24cGkQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.632.0", + "@aws-sdk/client-sts": "3.632.0", + "@aws-sdk/core": "3.629.0", + "@aws-sdk/credential-provider-node": "3.632.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.632.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.632.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.2", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.14", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.12", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.14", + "@smithy/util-defaults-mode-node": "^3.0.14", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sns": { + "version": "3.632.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sns/-/client-sns-3.632.0.tgz", + "integrity": "sha512-I8S0tFx26LL59/PiL4Reqna+LW7oIbRB+jygl4vrf4XIvpX5o4tmZmOmR0caYA+Cks3AomCi4cp+7WuwUpuATQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -700,7 +1382,6 @@ "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-sdk-sqs": "3.622.0", "@aws-sdk/middleware-user-agent": "3.632.0", "@aws-sdk/region-config-resolver": "3.614.0", "@aws-sdk/types": "3.609.0", @@ -712,7 +1393,6 @@ "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-node": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3", - "@smithy/md5-js": "^3.0.3", "@smithy/middleware-content-length": "^3.0.5", "@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-retry": "^3.0.14", @@ -1202,24 +1882,6 @@ "node": ">=16.0.0" } }, - "node_modules/@aws-sdk/middleware-sdk-sqs": { - "version": "3.622.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.622.0.tgz", - "integrity": "sha512-kOPX94jlVcvH7Wutzag99L+BSjT6LjXxW7Ntc02/oywYX6Gft4YdbeUYdcGYYHWDy/IT6jJ2wMJfFUEEh8U/9A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.609.0", - "@smithy/smithy-client": "^3.1.12", - "@smithy/types": "^3.3.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, "node_modules/@aws-sdk/middleware-ssec": { "version": "3.609.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.609.0.tgz", @@ -5618,12 +6280,13 @@ "license": "(Unlicense OR Apache-2.0)" }, "node_modules/@smithy/abort-controller": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.4.tgz", - "integrity": "sha512-VupaALAQlXViW3/enTf/f5l5JZYSAxoJL7f0nanhNNKnww6DGCg1oYIuNP78KDugnkwthBO6iEcym16HhWV8RQ==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.8.tgz", + "integrity": "sha512-+3DOBcUn5/rVjlxGvUPKc416SExarAQ+Qe0bqk30YSUjbepwpS7QN0cyKUSifvLJhdMZ0WPzPP5ymut0oonrpQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5652,15 +6315,16 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.8.tgz", - "integrity": "sha512-Tv1obAC18XOd2OnDAjSWmmthzx6Pdeh63FbLin8MlPiuJ2ATpKkq0NcNOJFr0dO+JmZXnwu8FQxKJ3TKJ3Hulw==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.12.tgz", + "integrity": "sha512-YAJP9UJFZRZ8N+UruTeq78zkdjUHmzsY62J4qKWZ4SXB4QXJ/+680EfXXgkYA2xj77ooMqtUY9m406zGNqwivQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.7", - "@smithy/types": "^3.4.2", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/types": "^3.7.1", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.6", + "@smithy/util-middleware": "^3.0.10", "tslib": "^2.6.2" }, "engines": { @@ -5668,19 +6332,18 @@ } }, "node_modules/@smithy/core": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.3.tgz", - "integrity": "sha512-4LTusLqFMRVQUfC3RNuTg6IzYTeJNpydRdTKq7J5wdEyIRQSu3rGIa3s80mgG2hhe6WOZl9IqTSo1pgbn6EHhA==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.4.tgz", + "integrity": "sha512-iFh2Ymn2sCziBRLPuOOxRPkuCx/2gBdXtBGuCUFLUe6bWYjKnhHyIPqGeNkLZ5Aco/5GjebRTBFiWID3sDbrKw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-endpoint": "^3.1.3", - "@smithy/middleware-retry": "^3.0.18", - "@smithy/middleware-serde": "^3.0.6", - "@smithy/protocol-http": "^4.1.3", - "@smithy/smithy-client": "^3.3.2", - "@smithy/types": "^3.4.2", + "@smithy/middleware-serde": "^3.0.10", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.6", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-stream": "^3.3.1", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -5689,15 +6352,16 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.3.tgz", - "integrity": "sha512-VoxMzSzdvkkjMJNE38yQgx4CfnmT+Z+5EUXkg4x7yag93eQkVQgZvN3XBSHC/ylfBbLbAtdu7flTCChX9I+mVg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.7.tgz", + "integrity": "sha512-cEfbau+rrWF8ylkmmVAObOmjbTIzKyUC5TkBL58SbLywD0RCBC4JAUKbmtSm2w5KUJNRPGgpGFMvE2FKnuNlWQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.7", - "@smithy/property-provider": "^3.1.6", - "@smithy/types": "^3.4.2", - "@smithy/url-parser": "^3.0.6", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/property-provider": "^3.1.10", + "@smithy/types": "^3.7.1", + "@smithy/url-parser": "^3.0.10", "tslib": "^2.6.2" }, "engines": { @@ -5705,25 +6369,27 @@ } }, "node_modules/@smithy/eventstream-codec": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.5.tgz", - "integrity": "sha512-6pu+PT2r+5ZnWEV3vLV1DzyrpJ0TmehQlniIDCSpZg6+Ji2SfOI38EqUyQ+O8lotVElCrfVc9chKtSMe9cmCZQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.9.tgz", + "integrity": "sha512-F574nX0hhlNOjBnP+noLtsPFqXnWh2L0+nZKCwcu7P7J8k+k+rdIDs+RMnrMwrzhUE4mwMgyN0cYnEn0G8yrnQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "@smithy/util-hex-encoding": "^3.0.0", "tslib": "^2.6.2" } }, "node_modules/@smithy/eventstream-serde-browser": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.9.tgz", - "integrity": "sha512-PiQLo6OQmZAotJweIcObL1H44gkvuJACKMNqpBBe5Rf2Ax1DOcGi/28+feZI7yTe1ERHlQQaGnm8sSkyDUgsMg==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.13.tgz", + "integrity": "sha512-Nee9m+97o9Qj6/XeLz2g2vANS2SZgAxV4rDBMKGHvFJHU/xz88x2RwCkwsvEwYjSX4BV1NG1JXmxEaDUzZTAtw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.8", - "@smithy/types": "^3.4.2", + "@smithy/eventstream-serde-universal": "^3.0.12", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5731,12 +6397,13 @@ } }, "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.6.tgz", - "integrity": "sha512-iew15It+c7WfnVowWkt2a7cdPp533LFJnpjDQgfZQcxv2QiOcyEcea31mnrk5PVbgo0nNH3VbYGq7myw2q/F6A==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.10.tgz", + "integrity": "sha512-K1M0x7P7qbBUKB0UWIL5KOcyi6zqV5mPJoL0/o01HPJr0CSq3A9FYuJC6e11EX6hR8QTIR++DBiGrYveOu6trw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5744,13 +6411,14 @@ } }, "node_modules/@smithy/eventstream-serde-node": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.8.tgz", - "integrity": "sha512-6m+wI+fT0na+6oao6UqALVA38fsScCpoG5UO/A8ZSyGLnPM2i4MS1cFUhpuALgvLMxfYoTCh7qSeJa0aG4IWpQ==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.12.tgz", + "integrity": "sha512-kiZymxXvZ4tnuYsPSMUHe+MMfc4FTeFWJIc0Q5wygJoUQM4rVHNghvd48y7ppuulNMbuYt95ah71pYc2+o4JOA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.8", - "@smithy/types": "^3.4.2", + "@smithy/eventstream-serde-universal": "^3.0.12", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5758,13 +6426,14 @@ } }, "node_modules/@smithy/eventstream-serde-universal": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.8.tgz", - "integrity": "sha512-09tqzIQ6e+7jLqGvRji1yJoDbL/zob0OFhq75edgStWErGLf16+yI5hRc/o9/YAybOhUZs/swpW2SPn892G5Gg==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.12.tgz", + "integrity": "sha512-1i8ifhLJrOZ+pEifTlF0EfZzMLUGQggYQ6WmZ4d5g77zEKf7oZ0kvh1yKWHPjofvOwqrkwRDVuxuYC8wVd662A==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-codec": "^3.1.5", - "@smithy/types": "^3.4.2", + "@smithy/eventstream-codec": "^3.1.9", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5798,12 +6467,13 @@ } }, "node_modules/@smithy/hash-node": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.6.tgz", - "integrity": "sha512-c/FHEdKK/7DU2z6ZE91L36ahyXWayR3B+FzELjnYq7wH5YqIseM24V+pWCS9kFn1Ln8OFGTf+pyYPiHZuX0s/Q==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.10.tgz", + "integrity": "sha512-3zWGWCHI+FlJ5WJwx73Mw2llYR8aflVyZN5JhoqLxbdPZi6UyKSdCeXAWJw9ja22m6S6Tzz1KZ+kAaSwvydi0g==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -5828,12 +6498,13 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.6.tgz", - "integrity": "sha512-czM7Ioq3s8pIXht7oD+vmgy4Wfb4XavU/k/irO8NdXFFOx7YAlsCCcKOh/lJD1mJSYQqiR7NmpZ9JviryD/7AQ==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.10.tgz", + "integrity": "sha512-Lp2L65vFi+cj0vFMu2obpPW69DU+6O5g3086lmI4XcnRCG8PxvpWC7XyaVwJCxsZFzueHjXnrOH/E0pl0zikfA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" } }, @@ -5863,13 +6534,14 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.8.tgz", - "integrity": "sha512-VuyszlSO49WKh3H9/kIO2kf07VUwGV80QRiaDxUfP8P8UKlokz381ETJvwLhwuypBYhLymCYyNhB3fLAGBX2og==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.12.tgz", + "integrity": "sha512-1mDEXqzM20yywaMDuf5o9ue8OkJ373lSPbaSjyEvkWdqELhFMyNNgKGWL/rCSf4KME8B+HlHKuR8u9kRj8HzEQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^4.1.3", - "@smithy/types": "^3.4.2", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5877,17 +6549,19 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.3.tgz", - "integrity": "sha512-KeM/OrK8MVFUsoJsmCN0MZMVPjKKLudn13xpgwIMpGTYpA8QZB2Xq5tJ+RE6iu3A6NhOI4VajDTwBsm8pwwrhg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.4.tgz", + "integrity": "sha512-TybiW2LA3kYVd3e+lWhINVu1o26KJbBwOpADnf0L4x/35vLVica77XVR5hvV9+kWeTGeSJ3IHTcYxbRxlbwhsg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^3.0.6", - "@smithy/node-config-provider": "^3.1.7", - "@smithy/shared-ini-file-loader": "^3.1.7", - "@smithy/types": "^3.4.2", - "@smithy/url-parser": "^3.0.6", - "@smithy/util-middleware": "^3.0.6", + "@smithy/core": "^2.5.4", + "@smithy/middleware-serde": "^3.0.10", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/shared-ini-file-loader": "^3.1.11", + "@smithy/types": "^3.7.1", + "@smithy/url-parser": "^3.0.10", + "@smithy/util-middleware": "^3.0.10", "tslib": "^2.6.2" }, "engines": { @@ -5895,18 +6569,19 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.18.tgz", - "integrity": "sha512-YU1o/vYob6vlqZdd97MN8cSXRToknLXhFBL3r+c9CZcnxkO/rgNZ++CfgX2vsmnEKvlqdi26+SRtSzlVp5z6Mg==", + "version": "3.0.28", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.28.tgz", + "integrity": "sha512-vK2eDfvIXG1U64FEUhYxoZ1JSj4XFbYWkK36iz02i3pFwWiDz1Q7jKhGTBCwx/7KqJNk4VS7d7cDLXFOvP7M+g==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.7", - "@smithy/protocol-http": "^4.1.3", - "@smithy/service-error-classification": "^3.0.6", - "@smithy/smithy-client": "^3.3.2", - "@smithy/types": "^3.4.2", - "@smithy/util-middleware": "^3.0.6", - "@smithy/util-retry": "^3.0.6", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/protocol-http": "^4.1.7", + "@smithy/service-error-classification": "^3.0.10", + "@smithy/smithy-client": "^3.4.5", + "@smithy/types": "^3.7.1", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-retry": "^3.0.10", "tslib": "^2.6.2", "uuid": "^9.0.1" }, @@ -5915,12 +6590,13 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.6.tgz", - "integrity": "sha512-KKTUSl1MzOM0MAjGbudeaVNtIDo+PpekTBkCNwvfZlKndodrnvRo+00USatiyLOc0ujjO9UydMRu3O9dYML7ag==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.10.tgz", + "integrity": "sha512-MnAuhh+dD14F428ubSJuRnmRsfOpxSzvRhaGVTvd/lrUDE3kxzCCmH8lnVTvoNQnV2BbJ4c15QwZ3UdQBtFNZA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5928,12 +6604,13 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.6.tgz", - "integrity": "sha512-2c0eSYhTQ8xQqHMcRxLMpadFbTXg6Zla5l0mwNftFCZMQmuhI7EbAJMx6R5eqfuV3YbJ3QGyS3d5uSmrHV8Khg==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.10.tgz", + "integrity": "sha512-grCHyoiARDBBGPyw2BeicpjgpsDFWZZxptbVKb3CRd/ZA15F/T6rZjCCuBUjJwdck1nwUuIxYtsS4H9DDpbP5w==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5941,14 +6618,15 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.7.tgz", - "integrity": "sha512-g3mfnC3Oo8pOI0dYuPXLtdW1WGVb3bR2tkV21GNkm0ZvQjLTtamXAwCWt/FCb0HGvKt3gHHmF1XerG0ICfalOg==", + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.11.tgz", + "integrity": "sha512-URq3gT3RpDikh/8MBJUB+QGZzfS7Bm6TQTqoh4CqE8NBuyPkWa5eUXj0XFcFfeZVgg3WMh1u19iaXn8FvvXxZw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^3.1.6", - "@smithy/shared-ini-file-loader": "^3.1.7", - "@smithy/types": "^3.4.2", + "@smithy/property-provider": "^3.1.10", + "@smithy/shared-ini-file-loader": "^3.1.11", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5956,15 +6634,16 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.2.2.tgz", - "integrity": "sha512-42Cy4/oT2O+00aiG1iQ7Kd7rE6q8j7vI0gFfnMlUiATvyo8vefJkhb7O10qZY0jAqo5WZdUzfl9IV6wQ3iMBCg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.1.tgz", + "integrity": "sha512-fr+UAOMGWh6bn4YSEezBCpJn9Ukp9oR4D32sCjCo7U81evE11YePOQ58ogzyfgmjIO79YeOdfXXqr0jyhPQeMg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^3.1.4", - "@smithy/protocol-http": "^4.1.3", - "@smithy/querystring-builder": "^3.0.6", - "@smithy/types": "^3.4.2", + "@smithy/abort-controller": "^3.1.8", + "@smithy/protocol-http": "^4.1.7", + "@smithy/querystring-builder": "^3.0.10", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5972,12 +6651,13 @@ } }, "node_modules/@smithy/property-provider": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.6.tgz", - "integrity": "sha512-NK3y/T7Q/Bw+Z8vsVs9MYIQ5v7gOX7clyrXcwhhIBQhbPgRl6JDrZbusO9qWDhcEus75Tg+VCxtIRfo3H76fpw==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.10.tgz", + "integrity": "sha512-n1MJZGTorTH2DvyTVj+3wXnd4CzjJxyXeOgnTlgNVFxaaMeT4OteEp4QrzF8p9ee2yg42nvyVK6R/awLCakjeQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5985,12 +6665,13 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.3.tgz", - "integrity": "sha512-GcbMmOYpH9iRqtC05RbRnc/0FssxSTHlmaNhYBTgSgNCYpdR3Kt88u5GAZTBmouzv+Zlj/VRv92J9ruuDeJuEw==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.7.tgz", + "integrity": "sha512-FP2LepWD0eJeOTm0SjssPcgqAlDFzOmRXqXmGhfIM52G7Lrox/pcpQf6RP4F21k0+O12zaqQt5fCDOeBtqY6Cg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5998,12 +6679,13 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.6.tgz", - "integrity": "sha512-sQe08RunoObe+Usujn9+R2zrLuQERi3CWvRO3BvnoWSYUaIrLKuAIeY7cMeDax6xGyfIP3x/yFWbEKSXvOnvVg==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.10.tgz", + "integrity": "sha512-nT9CQF3EIJtIUepXQuBFb8dxJi3WVZS3XfuDksxSCSn+/CzZowRLdhDn+2acbBv8R6eaJqPupoI/aRFIImNVPQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "@smithy/util-uri-escape": "^3.0.0", "tslib": "^2.6.2" }, @@ -6012,12 +6694,13 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.6.tgz", - "integrity": "sha512-UJKw4LlEkytzz2Wq+uIdHf6qOtFfee/o7ruH0jF5I6UAuU+19r9QV7nU3P/uI0l6+oElRHmG/5cBBcGJrD7Ozg==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.10.tgz", + "integrity": "sha512-Oa0XDcpo9SmjhiDD9ua2UyM3uU01ZTuIrNdZvzwUTykW1PM8o2yJvMh1Do1rY5sUQg4NDV70dMi0JhDx4GyxuQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -6025,24 +6708,26 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.6.tgz", - "integrity": "sha512-53SpchU3+DUZrN7J6sBx9tBiCVGzsib2e4sc512Q7K9fpC5zkJKs6Z9s+qbMxSYrkEkle6hnMtrts7XNkMJJMg==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.10.tgz", + "integrity": "sha512-zHe642KCqDxXLuhs6xmHVgRwy078RfqxP2wRDpIyiF8EmsWXptMwnMwbVa50lw+WOGNrYm9zbaEg0oDe3PTtvQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2" + "@smithy/types": "^3.7.1" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.7.tgz", - "integrity": "sha512-IA4K2qTJYXkF5OfVN4vsY1hfnUZjaslEE8Fsr/gGFza4TAC2A9NfnZuSY2srQIbt9bwtjHiAayrRVgKse4Q7fA==", + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.11.tgz", + "integrity": "sha512-AUdrIZHFtUgmfSN4Gq9nHu3IkHMa1YDcN+s061Nfm+6pQ0mJy85YQDB0tZBCmls0Vuj22pLwDPmL92+Hvfwwlg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -6050,16 +6735,17 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.3.tgz", - "integrity": "sha512-YD2KYSCEEeFHcWZ1E3mLdAaHl8T/TANh6XwmocQ6nPcTdBfh4N5fusgnblnWDlnlU1/cUqEq3PiGi22GmT2Lkg==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.3.tgz", + "integrity": "sha512-pPSQQ2v2vu9vc8iew7sszLd0O09I5TRc5zhY71KA+Ao0xYazIG+uLeHbTJfIWGO3BGVLiXjUr3EEeCcEQLjpWQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.3", - "@smithy/types": "^3.4.2", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.6", + "@smithy/util-middleware": "^3.0.10", "@smithy/util-uri-escape": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -6069,16 +6755,18 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.3.2.tgz", - "integrity": "sha512-RKDfhF2MTwXl7jan5d7QfS9eCC6XJbO3H+EZAvLQN8A5in4ib2Ml4zoeLo57w9QrqFekBPcsoC2hW3Ekw4vQ9Q==", + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.4.5.tgz", + "integrity": "sha512-k0sybYT9zlP79sIKd1XGm4TmK0AS1nA2bzDHXx7m0nGi3RQ8dxxQUs4CPkSmQTKAo+KF9aINU3KzpGIpV7UoMw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-endpoint": "^3.1.3", - "@smithy/middleware-stack": "^3.0.6", - "@smithy/protocol-http": "^4.1.3", - "@smithy/types": "^3.4.2", - "@smithy/util-stream": "^3.1.6", + "@smithy/core": "^2.5.4", + "@smithy/middleware-endpoint": "^3.2.4", + "@smithy/middleware-stack": "^3.0.10", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", + "@smithy/util-stream": "^3.3.1", "tslib": "^2.6.2" }, "engines": { @@ -6086,10 +6774,11 @@ } }, "node_modules/@smithy/types": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.4.2.tgz", - "integrity": "sha512-tHiFcfcVedVBHpmHUEUHOCCih8iZbIAYn9NvPsNzaPm/237I3imdDdZoOC8c87H5HBAVEa06tTgb+OcSWV9g5w==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.1.tgz", + "integrity": "sha512-XKLcLXZY7sUQgvvWyeaL/qwNPp6V3dWcUjqrQKjSb+tzYiCy340R/c64LV5j+Tnb2GhmunEX0eou+L+m2hJNYA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -6098,13 +6787,14 @@ } }, "node_modules/@smithy/url-parser": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.6.tgz", - "integrity": "sha512-47Op/NU8Opt49KyGpHtVdnmmJMsp2hEwBdyjuFB9M2V5QVOwA7pBhhxKN5z6ztKGrMw76gd8MlbPuzzvaAncuQ==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.10.tgz", + "integrity": "sha512-j90NUalTSBR2NaZTuruEgavSdh8MLirf58LoGSk4AtQfyIymogIhgnGUU2Mga2bkMkpSoC9gxb74xBXL5afKAQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^3.0.6", - "@smithy/types": "^3.4.2", + "@smithy/querystring-parser": "^3.0.10", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" } }, @@ -6174,14 +6864,15 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.18.tgz", - "integrity": "sha512-/eveCzU6Z6Yw8dlYQLA4rcK30XY0E4L3lD3QFHm59mzDaWYelrXE1rlynuT3J6qxv+5yNy3a1JuzhG5hk5hcmw==", + "version": "3.0.28", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.28.tgz", + "integrity": "sha512-6bzwAbZpHRFVJsOztmov5PGDmJYsbNSoIEfHSJJyFLzfBGCCChiO3od9k7E/TLgrCsIifdAbB9nqbVbyE7wRUw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^3.1.6", - "@smithy/smithy-client": "^3.3.2", - "@smithy/types": "^3.4.2", + "@smithy/property-provider": "^3.1.10", + "@smithy/smithy-client": "^3.4.5", + "@smithy/types": "^3.7.1", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -6190,17 +6881,18 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.18.tgz", - "integrity": "sha512-9cfzRjArtOFPlTYRREJk00suUxVXTgbrzVncOyMRTUeMKnecG/YentLF3cORa+R6mUOMSrMSnT18jos1PKqK6Q==", + "version": "3.0.28", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.28.tgz", + "integrity": "sha512-78ENJDorV1CjOQselGmm3+z7Yqjj5HWCbjzh0Ixuq736dh1oEnD9sAttSBNSLlpZsX8VQnmERqA2fEFlmqWn8w==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^3.0.8", - "@smithy/credential-provider-imds": "^3.2.3", - "@smithy/node-config-provider": "^3.1.7", - "@smithy/property-provider": "^3.1.6", - "@smithy/smithy-client": "^3.3.2", - "@smithy/types": "^3.4.2", + "@smithy/config-resolver": "^3.0.12", + "@smithy/credential-provider-imds": "^3.2.7", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/property-provider": "^3.1.10", + "@smithy/smithy-client": "^3.4.5", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -6208,13 +6900,14 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.2.tgz", - "integrity": "sha512-FEISzffb4H8DLzGq1g4MuDpcv6CIG15fXoQzDH9SjpRJv6h7J++1STFWWinilG0tQh9H1v2UKWG19Jjr2B16zQ==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.6.tgz", + "integrity": "sha512-mFV1t3ndBh0yZOJgWxO9J/4cHZVn5UG1D8DeCc6/echfNkeEJWu9LD7mgGH5fHrEdR7LDoWw7PQO6QiGpHXhgA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.7", - "@smithy/types": "^3.4.2", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -6235,12 +6928,13 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.6.tgz", - "integrity": "sha512-BxbX4aBhI1O9p87/xM+zWy0GzT3CEVcXFPBRDoHAM+pV0eSW156pR+PSYEz0DQHDMYDsYAflC2bQNz2uaDBUZQ==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.10.tgz", + "integrity": "sha512-eJO+/+RsrG2RpmY68jZdwQtnfsxjmPxzMlQpnHKjFPwrYqvlcT+fHdT+ZVwcjlWSrByOhGr9Ff2GG17efc192A==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -6248,13 +6942,14 @@ } }, "node_modules/@smithy/util-retry": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.6.tgz", - "integrity": "sha512-BRZiuF7IwDntAbevqMco67an0Sr9oLQJqqRCsSPZZHYRnehS0LHDAkJk/pSmI7Z8c/1Vet294H7fY2fWUgB+Rg==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.10.tgz", + "integrity": "sha512-1l4qatFp4PiU6j7UsbasUHL2VU023NRB/gfaa1M0rDqVrRN4g3mCArLRyH3OuktApA4ye+yjWQHjdziunw2eWA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^3.0.6", - "@smithy/types": "^3.4.2", + "@smithy/service-error-classification": "^3.0.10", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -6262,14 +6957,15 @@ } }, "node_modules/@smithy/util-stream": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.6.tgz", - "integrity": "sha512-lQEUfTx1ht5CRdvIjdAN/gUL6vQt2wSARGGLaBHNe+iJSkRHlWzY+DOn0mFTmTgyU3jcI5n9DkT5gTzYuSOo6A==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.1.tgz", + "integrity": "sha512-Ff68R5lJh2zj+AUTvbAU/4yx+6QPRzg7+pI7M1FbtQHcRIp7xvguxVsQBKyB3fwiOwhAKu0lnNyYBaQfSW6TNw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^3.2.7", - "@smithy/node-http-handler": "^3.2.2", - "@smithy/types": "^3.4.2", + "@smithy/fetch-http-handler": "^4.1.1", + "@smithy/node-http-handler": "^3.3.1", + "@smithy/types": "^3.7.1", "@smithy/util-base64": "^3.0.0", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-hex-encoding": "^3.0.0", @@ -6280,6 +6976,20 @@ "node": ">=16.0.0" } }, + "node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.1.tgz", + "integrity": "sha512-bH7QW0+JdX0bPBadXt8GwMof/jz0H28I84hU1Uet9ISpzUqXqRQ3fEZJ+ANPOhzSEczYvANNl3uDQDYArSFDtA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.7", + "@smithy/querystring-builder": "^3.0.10", + "@smithy/types": "^3.7.1", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, "node_modules/@smithy/util-uri-escape": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", @@ -6308,14 +7018,14 @@ } }, "node_modules/@smithy/util-waiter": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.5.tgz", - "integrity": "sha512-jYOSvM3H6sZe3CHjzD2VQNCjWBJs+4DbtwBMvUp9y5EnnwNa7NQxTeYeQw0CKCAdGGZ3QvVkyJmvbvs5M/B10A==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.9.tgz", + "integrity": "sha512-/aMXPANhMOlMPjfPtSrDfPeVP8l56SJlz93xeiLmhLe5xvlXA5T3abZ2ilEsDEPeY9T/wnN/vNGn9wa1SbufWA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^3.1.4", - "@smithy/types": "^3.4.2", + "@smithy/abort-controller": "^3.1.8", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { diff --git a/package.json b/package.json index d9c6872..627ffb2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aws-otel-js-instrumentation", - "version": "0.3.0-dev0", + "version": "0.4.0-dev0", "description": "This is a repository for AWS Distro of OpenTelemetry JavaScript SDK.", "repository": { "type": "git", diff --git a/version.txt b/version.txt deleted file mode 100644 index e69de29..0000000