From c00d3af817b692e0d8fc5a5ec54ae0645d7088a7 Mon Sep 17 00:00:00 2001
From: Michael He <53622546+yiyuan-he@users.noreply.github.com>
Date: Mon, 2 Dec 2024 15:25:43 -0800
Subject: [PATCH 1/2] Add Contract Tests for SecretsManager, StepFunctions, and
SNS (#958)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
*Description of changes:*
Adding contract tests for new AWS resources. Rebasing the commits in
this
[PR](https://github.com/aws-observability/aws-otel-java-instrumentation/pull/936/commits/8295fb06341a29e8da4f52bf8b9130f39c2c9bc8)
since there were some merge conflicts with Gen AI contract tests.
*Test plan:*
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
---
.../test/awssdk/base/AwsSdkBaseTest.java | 868 +++++++++++++++++-
.../test/awssdk/v1/AwsSdkV1Test.java | 80 ++
.../test/awssdk/v2/AwsSdkV2Test.java | 82 +-
.../test/utils/AppSignalsConstants.java | 2 +
.../utils/SemanticConventionsConstants.java | 5 +
.../aws-sdk/aws-sdk-v1/build.gradle.kts | 4 +
.../main/java/com/amazon/sampleapp/App.java | 387 ++++++++
.../aws-sdk/aws-sdk-v2/build.gradle.kts | 4 +
.../main/java/com/amazon/sampleapp/App.java | 385 ++++++++
9 files changed, 1810 insertions(+), 7 deletions(-)
diff --git a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/base/AwsSdkBaseTest.java b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/base/AwsSdkBaseTest.java
index 0f8652fb70..278aa4011d 100644
--- a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/base/AwsSdkBaseTest.java
+++ b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/base/AwsSdkBaseTest.java
@@ -42,7 +42,11 @@ public abstract class AwsSdkBaseTest extends ContractTestBase {
LocalStackContainer.Service.S3,
LocalStackContainer.Service.DYNAMODB,
LocalStackContainer.Service.SQS,
- LocalStackContainer.Service.KINESIS)
+ LocalStackContainer.Service.KINESIS,
+ LocalStackContainer.Service.SECRETSMANAGER,
+ LocalStackContainer.Service.IAM,
+ LocalStackContainer.Service.STEPFUNCTIONS,
+ LocalStackContainer.Service.SNS)
.withEnv("DEFAULT_REGION", "us-west-2")
.withNetwork(network)
.withEnv("LOCALSTACK_HOST", "127.0.0.1")
@@ -102,6 +106,12 @@ protected String getApplicationWaitPattern() {
protected abstract String getBedrockAgentRuntimeSpanNamePrefix();
+ protected abstract String getSecretsManagerSpanNamePrefix();
+
+ protected abstract String getStepFunctionsSpanNamePrefix();
+
+ protected abstract String getSnsSpanNamePrefix();
+
protected abstract String getS3RpcServiceName();
protected abstract String getDynamoDbRpcServiceName();
@@ -118,6 +128,12 @@ protected String getApplicationWaitPattern() {
protected abstract String getBedrockAgentRuntimeRpcServiceName();
+ protected abstract String getSecretsManagerRpcServiceName();
+
+ protected abstract String getSnsRpcServiceName();
+
+ protected abstract String getStepFunctionsRpcServiceName();
+
private String getS3ServiceName() {
return "AWS::S3";
}
@@ -150,6 +166,18 @@ private String getBedrockRuntimeServiceName() {
return "AWS::BedrockRuntime";
}
+ private String getSecretsManagerServiceName() {
+ return "AWS::SecretsManager";
+ }
+
+ private String getStepFunctionsServiceName() {
+ return "AWS::StepFunctions";
+ }
+
+ protected String getSnsServiceName() {
+ return "AWS::SNS";
+ }
+
private String s3SpanName(String operation) {
return String.format("%s.%s", getS3SpanNamePrefix(), operation);
}
@@ -182,10 +210,31 @@ private String bedrockAgentRuntimeSpanName(String operation) {
return String.format("%s.%s", getBedrockAgentRuntimeSpanNamePrefix(), operation);
}
+ private String secretsManagerSpanName(String operation) {
+ return String.format("%s.%s", getSecretsManagerSpanNamePrefix(), operation);
+ }
+
+ private String stepFunctionsSpanName(String operation) {
+ return String.format("%s.%s", getStepFunctionsSpanNamePrefix(), operation);
+ }
+
+ private String snsSpanName(String operation) {
+ return String.format("%s.%s", getSnsSpanNamePrefix(), operation);
+ }
+
protected ThrowingConsumer assertAttribute(String key, String value) {
return (attribute) -> {
- assertThat(attribute.getKey()).isEqualTo(key);
- assertThat(attribute.getValue().getStringValue()).isEqualTo(value);
+ var actualKey = attribute.getKey();
+ var actualValue = attribute.getValue().getStringValue();
+
+ assertThat(actualKey).isEqualTo(key);
+
+ // We only want to Regex Pattern Match on the Secret Id and Secret Arn
+ if (actualValue.contains("secret-id")) {
+ assertThat(actualValue).matches(value);
+ } else {
+ assertThat(actualValue).isEqualTo(value);
+ }
};
}
@@ -258,6 +307,7 @@ private void assertSpanClientAttributes(
String method,
String type,
String identifier,
+ String cloudformationIdentifier,
String peerName,
int peerPort,
String url,
@@ -276,6 +326,7 @@ private void assertSpanClientAttributes(
method,
type,
identifier,
+ cloudformationIdentifier,
peerName,
peerPort,
url,
@@ -293,6 +344,7 @@ private void assertSpanProducerAttributes(
String method,
String type,
String identifier,
+ String cloudformationIdentifier,
String peerName,
int peerPort,
String url,
@@ -310,6 +362,7 @@ private void assertSpanProducerAttributes(
method,
type,
identifier,
+ cloudformationIdentifier,
peerName,
peerPort,
url,
@@ -358,6 +411,7 @@ private void assertSpanAttributes(
String method,
String type,
String identifier,
+ String cloudformationIdentifier,
String peerName,
int peerPort,
String url,
@@ -371,6 +425,7 @@ private void assertSpanAttributes(
var spanAttributes = span.getAttributesList();
assertThat(span.getKind()).isEqualTo(spanKind);
assertThat(span.getName()).isEqualTo(spanName);
+
assertSemanticConventionsAttributes(
spanAttributes, rpcService, method, peerName, peerPort, url, statusCode);
assertAwsAttributes(
@@ -381,6 +436,7 @@ private void assertSpanAttributes(
method,
type,
identifier,
+ cloudformationIdentifier,
awsSpanKind);
for (var assertion : extraAssertions) {
assertThat(spanAttributes).satisfiesOnlyOnce(assertion);
@@ -396,6 +452,7 @@ private void assertAwsAttributes(
String operation,
String type,
String identifier,
+ String clouformationIdentifier,
String spanKind) {
var assertions =
@@ -406,11 +463,14 @@ private void assertAwsAttributes(
.satisfiesOnlyOnce(assertAttribute(AppSignalsConstants.AWS_REMOTE_OPERATION, operation))
.satisfiesOnlyOnce(assertAttribute(AppSignalsConstants.AWS_REMOTE_SERVICE, service))
.satisfiesOnlyOnce(assertAttribute(AppSignalsConstants.AWS_SPAN_KIND, spanKind));
- if (type != null && identifier != null) {
+ if (type != null && identifier != null && clouformationIdentifier != null) {
assertions.satisfiesOnlyOnce(
assertAttribute(AppSignalsConstants.AWS_REMOTE_RESOURCE_TYPE, type));
assertions.satisfiesOnlyOnce(
assertAttribute(AppSignalsConstants.AWS_REMOTE_RESOURCE_IDENTIFIER, identifier));
+ assertions.satisfiesOnlyOnce(
+ assertAttribute(
+ AppSignalsConstants.AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER, clouformationIdentifier));
}
}
@@ -435,6 +495,7 @@ protected void assertMetricClientAttributes(
String method,
String type,
String identifier,
+ String cloudformationIdentifier,
Double expectedSum) {
assertMetricAttributes(
resourceScopeMetrics,
@@ -446,6 +507,7 @@ protected void assertMetricClientAttributes(
method,
type,
identifier,
+ cloudformationIdentifier,
expectedSum);
}
@@ -458,6 +520,7 @@ protected void assertMetricProducerAttributes(
String method,
String type,
String identifier,
+ String cloudformationIdentifier,
Double expectedSum) {
assertMetricAttributes(
resourceScopeMetrics,
@@ -469,6 +532,7 @@ protected void assertMetricProducerAttributes(
method,
type,
identifier,
+ cloudformationIdentifier,
expectedSum);
}
@@ -481,6 +545,7 @@ protected void assertMetricConsumerAttributes(
String method,
String type,
String identifier,
+ String cloudformationIdentifier,
Double expectedSum) {
assertMetricAttributes(
resourceScopeMetrics,
@@ -492,6 +557,7 @@ protected void assertMetricConsumerAttributes(
method,
type,
identifier,
+ cloudformationIdentifier,
expectedSum);
}
@@ -505,6 +571,7 @@ protected void assertMetricAttributes(
String method,
String type,
String identifier,
+ String cloudformationIdentifier,
Double expectedSum) {
assertThat(resourceScopeMetrics)
.anySatisfy(
@@ -524,6 +591,7 @@ protected void assertMetricAttributes(
method,
type,
identifier,
+ cloudformationIdentifier,
spanKind);
if (expectedSum != null) {
double actualSum = dataPoint.getSum();
@@ -554,6 +622,7 @@ protected void doTestS3CreateBucket() throws Exception {
var localOperation = "GET /s3/createbucket/:bucketname";
var type = "AWS::S3::Bucket";
var identifier = "create-bucket";
+ var cloudformationIdentifier = "create-bucket";
assertSpanClientAttributes(
traces,
@@ -565,6 +634,7 @@ protected void doTestS3CreateBucket() throws Exception {
"CreateBucket",
type,
identifier,
+ cloudformationIdentifier,
"create-bucket.s3.localstack",
4566,
"http://create-bucket.s3.localstack:4566",
@@ -579,6 +649,7 @@ protected void doTestS3CreateBucket() throws Exception {
"CreateBucket",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -589,6 +660,7 @@ protected void doTestS3CreateBucket() throws Exception {
"CreateBucket",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -599,6 +671,7 @@ protected void doTestS3CreateBucket() throws Exception {
"CreateBucket",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -617,6 +690,7 @@ protected void doTestS3CreateObject() throws Exception {
var localOperation = "GET /s3/createobject/:bucketname/:objectname";
var type = "AWS::S3::Bucket";
var identifier = "put-object";
+ var cloudformationIdentifier = "put-object";
assertSpanClientAttributes(
traces,
@@ -628,6 +702,7 @@ protected void doTestS3CreateObject() throws Exception {
"PutObject",
type,
identifier,
+ cloudformationIdentifier,
"put-object.s3.localstack",
4566,
"http://put-object.s3.localstack:4566",
@@ -642,6 +717,7 @@ protected void doTestS3CreateObject() throws Exception {
"PutObject",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -652,6 +728,7 @@ protected void doTestS3CreateObject() throws Exception {
"PutObject",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -662,6 +739,7 @@ protected void doTestS3CreateObject() throws Exception {
"PutObject",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -679,6 +757,7 @@ protected void doTestS3GetObject() throws Exception {
var localOperation = "GET /s3/getobject/:bucketName/:objectname";
var type = "AWS::S3::Bucket";
var identifier = "get-object";
+ var cloudformationIdentifier = "get-object";
assertSpanClientAttributes(
traces,
@@ -690,6 +769,7 @@ protected void doTestS3GetObject() throws Exception {
"GetObject",
type,
identifier,
+ cloudformationIdentifier,
"get-object.s3.localstack",
4566,
"http://get-object.s3.localstack:4566",
@@ -704,6 +784,7 @@ protected void doTestS3GetObject() throws Exception {
"GetObject",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -714,6 +795,7 @@ protected void doTestS3GetObject() throws Exception {
"GetObject",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -724,6 +806,7 @@ protected void doTestS3GetObject() throws Exception {
"GetObject",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -741,6 +824,7 @@ protected void doTestS3Error() {
var localOperation = "GET /s3/error";
var type = "AWS::S3::Bucket";
var identifier = "error-bucket";
+ var cloudformationIdentifier = "error-bucket";
assertSpanClientAttributes(
traces,
@@ -752,6 +836,7 @@ protected void doTestS3Error() {
"GetObject",
type,
identifier,
+ cloudformationIdentifier,
"error-bucket.s3.test",
8080,
"http://error-bucket.s3.test:8080",
@@ -766,6 +851,7 @@ protected void doTestS3Error() {
"GetObject",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -776,6 +862,7 @@ protected void doTestS3Error() {
"GetObject",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -786,6 +873,7 @@ protected void doTestS3Error() {
"GetObject",
type,
identifier,
+ cloudformationIdentifier,
1.0);
}
@@ -803,6 +891,7 @@ protected void doTestS3Fault() {
var localOperation = "GET /s3/fault";
var type = "AWS::S3::Bucket";
var identifier = "fault-bucket";
+ var cloudformationIdentifier = "fault-bucket";
assertSpanClientAttributes(
traces,
@@ -814,6 +903,7 @@ protected void doTestS3Fault() {
"GetObject",
type,
identifier,
+ cloudformationIdentifier,
"fault-bucket.s3.test",
8080,
"http://fault-bucket.s3.test:8080",
@@ -828,6 +918,7 @@ protected void doTestS3Fault() {
"GetObject",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -838,6 +929,7 @@ protected void doTestS3Fault() {
"GetObject",
type,
identifier,
+ cloudformationIdentifier,
1.0);
assertMetricClientAttributes(
metrics,
@@ -848,6 +940,7 @@ protected void doTestS3Fault() {
"GetObject",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -873,6 +966,7 @@ protected void doTestDynamoDbCreateTable() {
var localOperation = "GET /ddb/createtable/:tablename";
var type = "AWS::DynamoDB::Table";
var identifier = "some-table";
+ var cloudformationIdentifier = "some-table";
assertSpanClientAttributes(
traces,
@@ -884,6 +978,7 @@ protected void doTestDynamoDbCreateTable() {
"CreateTable",
type,
identifier,
+ cloudformationIdentifier,
"localstack",
4566,
"http://localstack:4566",
@@ -898,6 +993,7 @@ protected void doTestDynamoDbCreateTable() {
"CreateTable",
type,
identifier,
+ cloudformationIdentifier,
20000.0);
assertMetricClientAttributes(
metrics,
@@ -908,6 +1004,7 @@ protected void doTestDynamoDbCreateTable() {
"CreateTable",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -918,6 +1015,7 @@ protected void doTestDynamoDbCreateTable() {
"CreateTable",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -935,6 +1033,7 @@ protected void doTestDynamoDbPutItem() {
var localOperation = "GET /ddb/putitem/:tablename/:partitionkey";
var type = "AWS::DynamoDB::Table";
var identifier = "putitem-table";
+ var cloudformationIdentifier = "putitem-table";
assertSpanClientAttributes(
traces,
@@ -946,6 +1045,7 @@ protected void doTestDynamoDbPutItem() {
"PutItem",
type,
identifier,
+ cloudformationIdentifier,
"localstack",
4566,
"http://localstack:4566",
@@ -960,6 +1060,7 @@ protected void doTestDynamoDbPutItem() {
"PutItem",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -970,6 +1071,7 @@ protected void doTestDynamoDbPutItem() {
"PutItem",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -980,6 +1082,7 @@ protected void doTestDynamoDbPutItem() {
"PutItem",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -997,6 +1100,7 @@ protected void doTestDynamoDbError() throws Exception {
var localOperation = "GET /ddb/error";
var type = "AWS::DynamoDB::Table";
var identifier = "nonexistanttable";
+ var cloudformationIdentifier = "nonexistanttable";
assertSpanClientAttributes(
traces,
@@ -1008,6 +1112,7 @@ protected void doTestDynamoDbError() throws Exception {
"PutItem",
type,
identifier,
+ cloudformationIdentifier,
"error.test",
8080,
"http://error.test:8080",
@@ -1022,6 +1127,7 @@ protected void doTestDynamoDbError() throws Exception {
"PutItem",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -1032,6 +1138,7 @@ protected void doTestDynamoDbError() throws Exception {
"PutItem",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -1042,6 +1149,7 @@ protected void doTestDynamoDbError() throws Exception {
"PutItem",
type,
identifier,
+ cloudformationIdentifier,
1.0);
}
@@ -1065,6 +1173,7 @@ protected void doTestDynamoDbFault() throws Exception {
var localOperation = "GET /ddb/fault";
var type = "AWS::DynamoDB::Table";
var identifier = "nonexistanttable";
+ var cloudformationIdentifier = "nonexistanttable";
assertSpanClientAttributes(
traces,
@@ -1076,6 +1185,7 @@ protected void doTestDynamoDbFault() throws Exception {
"PutItem",
type,
identifier,
+ cloudformationIdentifier,
"fault.test",
8080,
"http://fault.test:8080",
@@ -1090,6 +1200,7 @@ protected void doTestDynamoDbFault() throws Exception {
"PutItem",
type,
identifier,
+ cloudformationIdentifier,
20000.0);
assertMetricClientAttributes(
metrics,
@@ -1100,6 +1211,7 @@ protected void doTestDynamoDbFault() throws Exception {
"PutItem",
type,
identifier,
+ cloudformationIdentifier,
1.0);
assertMetricClientAttributes(
metrics,
@@ -1110,6 +1222,7 @@ protected void doTestDynamoDbFault() throws Exception {
"PutItem",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -1127,6 +1240,7 @@ protected void doTestSQSCreateQueue() throws Exception {
var localOperation = "GET /sqs/createqueue/:queuename";
var type = "AWS::SQS::Queue";
var identifier = "some-queue";
+ var cloudformationIdentifier = "some-queue";
assertSpanClientAttributes(
traces,
@@ -1138,6 +1252,7 @@ protected void doTestSQSCreateQueue() throws Exception {
"CreateQueue",
type,
identifier,
+ cloudformationIdentifier,
"localstack",
4566,
"http://localstack:4566",
@@ -1152,6 +1267,7 @@ protected void doTestSQSCreateQueue() throws Exception {
"CreateQueue",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -1162,6 +1278,7 @@ protected void doTestSQSCreateQueue() throws Exception {
"CreateQueue",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -1172,6 +1289,7 @@ protected void doTestSQSCreateQueue() throws Exception {
"CreateQueue",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -1190,6 +1308,7 @@ protected void doTestSQSSendMessage() throws Exception {
// SendMessage does not capture aws.queue.name
String type = null;
String identifier = null;
+ String cloudformationIdentifier = null;
assertSpanProducerAttributes(
traces,
@@ -1201,6 +1320,7 @@ protected void doTestSQSSendMessage() throws Exception {
"SendMessage",
type,
identifier,
+ cloudformationIdentifier,
"localstack",
4566,
"http://localstack:4566",
@@ -1216,6 +1336,7 @@ protected void doTestSQSSendMessage() throws Exception {
"SendMessage",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricProducerAttributes(
metrics,
@@ -1226,6 +1347,7 @@ protected void doTestSQSSendMessage() throws Exception {
"SendMessage",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricProducerAttributes(
metrics,
@@ -1236,6 +1358,7 @@ protected void doTestSQSSendMessage() throws Exception {
"SendMessage",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -1258,6 +1381,7 @@ protected void doTestSQSReceiveMessage() throws Exception {
// ReceiveMessage does not capture aws.queue.name
String type = null;
String identifier = null;
+ String cloudformationIdentifier = null;
// Consumer traces for SQS behave like a Server span (they create the local aws service
// attributes), but have RPC attributes like a client span.
assertSpanConsumerAttributes(
@@ -1282,6 +1406,7 @@ protected void doTestSQSReceiveMessage() throws Exception {
"ReceiveMessage",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricConsumerAttributes(
metrics,
@@ -1292,6 +1417,7 @@ protected void doTestSQSReceiveMessage() throws Exception {
"ReceiveMessage",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -1310,6 +1436,7 @@ protected void doTestSQSError() throws Exception {
// SendMessage does not capture aws.queue.name
String type = null;
String identifier = null;
+ String cloudformationIdentifier = null;
assertSpanProducerAttributes(
traces,
@@ -1321,6 +1448,7 @@ protected void doTestSQSError() throws Exception {
"SendMessage",
type,
identifier,
+ cloudformationIdentifier,
"error.test",
8080,
"http://error.test:8080",
@@ -1336,6 +1464,7 @@ protected void doTestSQSError() throws Exception {
"SendMessage",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricProducerAttributes(
metrics,
@@ -1346,6 +1475,7 @@ protected void doTestSQSError() throws Exception {
"SendMessage",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricProducerAttributes(
metrics,
@@ -1356,6 +1486,7 @@ protected void doTestSQSError() throws Exception {
"SendMessage",
type,
identifier,
+ cloudformationIdentifier,
1.0);
}
@@ -1374,6 +1505,7 @@ protected void doTestSQSFault() throws Exception {
// SendMessage does not capture aws.queue.name
String type = null;
String identifier = null;
+ String cloudformationIdentifier = null;
assertSpanProducerAttributes(
traces,
@@ -1385,6 +1517,7 @@ protected void doTestSQSFault() throws Exception {
"SendMessage",
type,
identifier,
+ cloudformationIdentifier,
"fault.test",
8080,
"http://fault.test:8080",
@@ -1400,6 +1533,7 @@ protected void doTestSQSFault() throws Exception {
"SendMessage",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricProducerAttributes(
metrics,
@@ -1410,6 +1544,7 @@ protected void doTestSQSFault() throws Exception {
"SendMessage",
type,
identifier,
+ cloudformationIdentifier,
1.0);
assertMetricProducerAttributes(
metrics,
@@ -1420,6 +1555,7 @@ protected void doTestSQSFault() throws Exception {
"SendMessage",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -1437,6 +1573,7 @@ protected void doTestKinesisPutRecord() throws Exception {
var localOperation = "GET /kinesis/putrecord/:streamname";
var type = "AWS::Kinesis::Stream";
var identifier = "my-stream";
+ var cloudformationIdentifier = "my-stream";
assertSpanClientAttributes(
traces,
@@ -1448,6 +1585,7 @@ protected void doTestKinesisPutRecord() throws Exception {
"PutRecord",
type,
identifier,
+ cloudformationIdentifier,
"localstack",
4566,
"http://localstack:4566",
@@ -1462,6 +1600,7 @@ protected void doTestKinesisPutRecord() throws Exception {
"PutRecord",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -1472,6 +1611,7 @@ protected void doTestKinesisPutRecord() throws Exception {
"PutRecord",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -1482,6 +1622,7 @@ protected void doTestKinesisPutRecord() throws Exception {
"PutRecord",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -1499,6 +1640,7 @@ protected void doTestKinesisError() throws Exception {
var localOperation = "GET /kinesis/error";
var type = "AWS::Kinesis::Stream";
var identifier = "nonexistantstream";
+ var cloudformationIdentifier = "nonexistantstream";
assertSpanClientAttributes(
traces,
@@ -1510,6 +1652,7 @@ protected void doTestKinesisError() throws Exception {
"PutRecord",
type,
identifier,
+ cloudformationIdentifier,
"error.test",
8080,
"http://error.test:8080",
@@ -1525,6 +1668,7 @@ protected void doTestKinesisError() throws Exception {
"PutRecord",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -1535,6 +1679,7 @@ protected void doTestKinesisError() throws Exception {
"PutRecord",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -1545,6 +1690,7 @@ protected void doTestKinesisError() throws Exception {
"PutRecord",
type,
identifier,
+ cloudformationIdentifier,
1.0);
}
@@ -1562,6 +1708,7 @@ protected void doTestKinesisFault() throws Exception {
var localOperation = "GET /kinesis/fault";
var type = "AWS::Kinesis::Stream";
var identifier = "faultstream";
+ var cloudformationIdentifier = "faultstream";
assertSpanClientAttributes(
traces,
@@ -1573,6 +1720,7 @@ protected void doTestKinesisFault() throws Exception {
"PutRecord",
type,
identifier,
+ cloudformationIdentifier,
"fault.test",
8080,
"http://fault.test:8080",
@@ -1587,6 +1735,7 @@ protected void doTestKinesisFault() throws Exception {
"PutRecord",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -1597,6 +1746,7 @@ protected void doTestKinesisFault() throws Exception {
"PutRecord",
type,
identifier,
+ cloudformationIdentifier,
1.0);
assertMetricClientAttributes(
metrics,
@@ -1607,6 +1757,7 @@ protected void doTestKinesisFault() throws Exception {
"PutRecord",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -1625,6 +1776,7 @@ protected void doTestBedrockAgentKnowledgeBaseId() {
var localOperation = "GET /bedrockagent/getknowledgeBase/:knowledgeBaseId";
String type = "AWS::Bedrock::KnowledgeBase";
String identifier = "knowledge-base-id";
+ String cloudformationIdentifier = "knowledge-base-id";
assertSpanClientAttributes(
traces,
bedrockAgentSpanName("GetKnowledgeBase"),
@@ -1635,6 +1787,7 @@ protected void doTestBedrockAgentKnowledgeBaseId() {
"GetKnowledgeBase",
type,
identifier,
+ cloudformationIdentifier,
"bedrock.test",
8080,
"http://bedrock.test:8080",
@@ -1651,6 +1804,7 @@ protected void doTestBedrockAgentKnowledgeBaseId() {
"GetKnowledgeBase",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -1661,6 +1815,7 @@ protected void doTestBedrockAgentKnowledgeBaseId() {
"GetKnowledgeBase",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -1671,6 +1826,7 @@ protected void doTestBedrockAgentKnowledgeBaseId() {
"GetKnowledgeBase",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -1688,6 +1844,7 @@ protected void doTestBedrockAgentAgentId() {
var localOperation = "GET /bedrockagent/getagent/:agentId";
String type = "AWS::Bedrock::Agent";
String identifier = "test-agent-id";
+ String cloudformationIdentifier = "test-agent-id";
assertSpanClientAttributes(
traces,
bedrockAgentSpanName("GetAgent"),
@@ -1698,6 +1855,7 @@ protected void doTestBedrockAgentAgentId() {
"GetAgent",
type,
identifier,
+ cloudformationIdentifier,
"bedrock.test",
8080,
"http://bedrock.test:8080",
@@ -1712,6 +1870,7 @@ protected void doTestBedrockAgentAgentId() {
"GetAgent",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -1722,6 +1881,7 @@ protected void doTestBedrockAgentAgentId() {
"GetAgent",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -1732,6 +1892,7 @@ protected void doTestBedrockAgentAgentId() {
"GetAgent",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -1749,6 +1910,7 @@ protected void doTestBedrockAgentDataSourceId() {
var localOperation = "GET /bedrockagent/get-data-source";
String type = "AWS::Bedrock::DataSource";
String identifier = "nonExistDatasourceId";
+ String cloudformationIdentifier = "nonExistDatasourceId";
assertSpanClientAttributes(
traces,
bedrockAgentSpanName("GetDataSource"),
@@ -1759,6 +1921,7 @@ protected void doTestBedrockAgentDataSourceId() {
"GetDataSource",
type,
identifier,
+ cloudformationIdentifier,
"bedrock.test",
8080,
"http://bedrock.test:8080",
@@ -1775,6 +1938,7 @@ protected void doTestBedrockAgentDataSourceId() {
"GetDataSource",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -1785,6 +1949,7 @@ protected void doTestBedrockAgentDataSourceId() {
"GetDataSource",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -1795,6 +1960,7 @@ protected void doTestBedrockAgentDataSourceId() {
"GetDataSource",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -1812,6 +1978,7 @@ protected void doTestBedrockRuntimeAi21Jamba() {
var localOperation = "GET /bedrockruntime/invokeModel/ai21Jamba";
String type = "AWS::Bedrock::Model";
String identifier = "ai21.jamba-1-5-mini-v1:0";
+ String cloudformationIdentifier = "ai21.jamba-1-5-mini-v1:0";
assertSpanClientAttributes(
traces,
bedrockRuntimeSpanName("InvokeModel"),
@@ -1822,6 +1989,7 @@ protected void doTestBedrockRuntimeAi21Jamba() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
"bedrock.test",
8080,
"http://bedrock.test:8080",
@@ -1843,6 +2011,7 @@ protected void doTestBedrockRuntimeAi21Jamba() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -1853,6 +2022,7 @@ protected void doTestBedrockRuntimeAi21Jamba() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -1863,6 +2033,7 @@ protected void doTestBedrockRuntimeAi21Jamba() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -1880,6 +2051,7 @@ protected void doTestBedrockRuntimeAmazonTitan() {
var localOperation = "GET /bedrockruntime/invokeModel/amazonTitan";
String type = "AWS::Bedrock::Model";
String identifier = "amazon.titan-text-premier-v1:0";
+ String cloudformationIdentifier = "amazon.titan-text-premier-v1:0";
assertSpanClientAttributes(
traces,
bedrockRuntimeSpanName("InvokeModel"),
@@ -1890,6 +2062,7 @@ protected void doTestBedrockRuntimeAmazonTitan() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
"bedrock.test",
8080,
"http://bedrock.test:8080",
@@ -1914,6 +2087,7 @@ protected void doTestBedrockRuntimeAmazonTitan() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -1924,6 +2098,7 @@ protected void doTestBedrockRuntimeAmazonTitan() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -1934,6 +2109,7 @@ protected void doTestBedrockRuntimeAmazonTitan() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -1952,6 +2128,7 @@ protected void doTestBedrockRuntimeAnthropicClaude() {
var localOperation = "GET /bedrockruntime/invokeModel/anthropicClaude";
String type = "AWS::Bedrock::Model";
String identifier = "anthropic.claude-3-haiku-20240307-v1:0";
+ String cloudformationIdentifier = "anthropic.claude-3-haiku-20240307-v1:0";
assertSpanClientAttributes(
traces,
@@ -1963,6 +2140,7 @@ protected void doTestBedrockRuntimeAnthropicClaude() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
"bedrock.test",
8080,
"http://bedrock.test:8080",
@@ -1987,6 +2165,7 @@ protected void doTestBedrockRuntimeAnthropicClaude() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -1997,6 +2176,7 @@ protected void doTestBedrockRuntimeAnthropicClaude() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -2007,6 +2187,7 @@ protected void doTestBedrockRuntimeAnthropicClaude() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -2025,6 +2206,7 @@ protected void doTestBedrockRuntimeCohereCommandR() {
var localOperation = "GET /bedrockruntime/invokeModel/cohereCommandR";
String type = "AWS::Bedrock::Model";
String identifier = "cohere.command-r-v1:0";
+ String cloudformationIdentifier = "cohere.command-r-v1:0";
assertSpanClientAttributes(
traces,
@@ -2036,6 +2218,7 @@ protected void doTestBedrockRuntimeCohereCommandR() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
"bedrock.test",
8080,
"http://bedrock.test:8080",
@@ -2059,6 +2242,7 @@ protected void doTestBedrockRuntimeCohereCommandR() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -2069,6 +2253,7 @@ protected void doTestBedrockRuntimeCohereCommandR() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -2079,6 +2264,7 @@ protected void doTestBedrockRuntimeCohereCommandR() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -2097,6 +2283,7 @@ protected void doTestBedrockRuntimeMetaLlama() {
var localOperation = "GET /bedrockruntime/invokeModel/metaLlama";
String type = "AWS::Bedrock::Model";
String identifier = "meta.llama3-70b-instruct-v1:0";
+ String cloudformationIdentifier = "meta.llama3-70b-instruct-v1:0";
assertSpanClientAttributes(
traces,
@@ -2108,6 +2295,7 @@ protected void doTestBedrockRuntimeMetaLlama() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
"bedrock.test",
8080,
"http://bedrock.test:8080",
@@ -2130,6 +2318,7 @@ protected void doTestBedrockRuntimeMetaLlama() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -2140,6 +2329,7 @@ protected void doTestBedrockRuntimeMetaLlama() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -2150,6 +2340,7 @@ protected void doTestBedrockRuntimeMetaLlama() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -2168,6 +2359,7 @@ protected void doTestBedrockRuntimeMistral() {
var localOperation = "GET /bedrockruntime/invokeModel/mistralAi";
String type = "AWS::Bedrock::Model";
String identifier = "mistral.mistral-large-2402-v1:0";
+ String cloudformationIdentifier = "mistral.mistral-large-2402-v1:0";
assertSpanClientAttributes(
traces,
@@ -2179,6 +2371,7 @@ protected void doTestBedrockRuntimeMistral() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
"bedrock.test",
8080,
"http://bedrock.test:8080",
@@ -2202,6 +2395,7 @@ protected void doTestBedrockRuntimeMistral() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -2212,6 +2406,7 @@ protected void doTestBedrockRuntimeMistral() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -2222,6 +2417,7 @@ protected void doTestBedrockRuntimeMistral() {
"InvokeModel",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -2239,6 +2435,8 @@ protected void doTestBedrockGuardrailId() {
var localOperation = "GET /bedrock/getguardrail";
String type = "AWS::Bedrock::Guardrail";
String identifier = "test-bedrock-guardrail";
+ String cloudformationIdentifier =
+ "arn:aws:bedrock:us-east-1:000000000000:guardrail/test-bedrock-guardrail";
assertSpanClientAttributes(
traces,
bedrockSpanName("GetGuardrail"),
@@ -2249,13 +2447,17 @@ protected void doTestBedrockGuardrailId() {
"GetGuardrail",
type,
identifier,
+ cloudformationIdentifier,
"bedrock.test",
8080,
"http://bedrock.test:8080",
200,
List.of(
assertAttribute(
- SemanticConventionsConstants.AWS_GUARDRAIL_ID, "test-bedrock-guardrail")));
+ SemanticConventionsConstants.AWS_GUARDRAIL_ID, "test-bedrock-guardrail"),
+ assertAttribute(
+ SemanticConventionsConstants.AWS_GUARDRAIL_ARN,
+ "arn:aws:bedrock:us-east-1:000000000000:guardrail/test-bedrock-guardrail")));
assertMetricClientAttributes(
metrics,
AppSignalsConstants.LATENCY_METRIC,
@@ -2265,6 +2467,7 @@ protected void doTestBedrockGuardrailId() {
"GetGuardrail",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -2275,6 +2478,7 @@ protected void doTestBedrockGuardrailId() {
"GetGuardrail",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -2285,6 +2489,7 @@ protected void doTestBedrockGuardrailId() {
"GetGuardrail",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -2302,6 +2507,7 @@ protected void doTestBedrockAgentRuntimeAgentId() {
var localOperation = "GET /bedrockagentruntime/getmemory/:agentId";
String type = "AWS::Bedrock::Agent";
String identifier = "test-agent-id";
+ String cloudformationIdentifier = "test-agent-id";
assertSpanClientAttributes(
traces,
bedrockAgentRuntimeSpanName("GetAgentMemory"),
@@ -2312,6 +2518,7 @@ protected void doTestBedrockAgentRuntimeAgentId() {
"GetAgentMemory",
type,
identifier,
+ cloudformationIdentifier,
"bedrock.test",
8080,
"http://bedrock.test:8080",
@@ -2326,6 +2533,7 @@ protected void doTestBedrockAgentRuntimeAgentId() {
"GetAgentMemory",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -2336,6 +2544,7 @@ protected void doTestBedrockAgentRuntimeAgentId() {
"GetAgentMemory",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -2346,6 +2555,7 @@ protected void doTestBedrockAgentRuntimeAgentId() {
"GetAgentMemory",
type,
identifier,
+ cloudformationIdentifier,
0.0);
}
@@ -2364,6 +2574,7 @@ protected void doTestBedrockAgentRuntimeKnowledgeBaseId() {
var localOperation = "GET /bedrockagentruntime/retrieve/:knowledgeBaseId";
String type = "AWS::Bedrock::KnowledgeBase";
String identifier = "test-knowledge-base-id";
+ String cloudformationIdentifier = "test-knowledge-base-id";
assertSpanClientAttributes(
traces,
bedrockAgentRuntimeSpanName("Retrieve"),
@@ -2374,6 +2585,7 @@ protected void doTestBedrockAgentRuntimeKnowledgeBaseId() {
"Retrieve",
type,
identifier,
+ cloudformationIdentifier,
"bedrock.test",
8080,
"http://bedrock.test:8080",
@@ -2390,6 +2602,7 @@ protected void doTestBedrockAgentRuntimeKnowledgeBaseId() {
"Retrieve",
type,
identifier,
+ cloudformationIdentifier,
5000.0);
assertMetricClientAttributes(
metrics,
@@ -2400,6 +2613,7 @@ protected void doTestBedrockAgentRuntimeKnowledgeBaseId() {
"Retrieve",
type,
identifier,
+ cloudformationIdentifier,
0.0);
assertMetricClientAttributes(
metrics,
@@ -2410,6 +2624,650 @@ protected void doTestBedrockAgentRuntimeKnowledgeBaseId() {
"Retrieve",
type,
identifier,
+ cloudformationIdentifier,
+ 0.0);
+ }
+
+ protected void doTestSecretsManagerDescribeSecret() throws Exception {
+ appClient.get("/secretsmanager/describesecret/test-secret-id").aggregate().join();
+ var traces = mockCollectorClient.getTraces();
+ var metrics =
+ mockCollectorClient.getMetrics(
+ Set.of(
+ AppSignalsConstants.ERROR_METRIC,
+ AppSignalsConstants.FAULT_METRIC,
+ AppSignalsConstants.LATENCY_METRIC));
+ var localService = getApplicationOtelServiceName();
+ var localOperation = "GET /secretsmanager/describesecret/:secretId";
+ var type = "AWS::SecretsManager::Secret";
+ var identifier = "test-secret-id-[A-Za-z0-9]{6}";
+ var cloudformationIdentifier =
+ "arn:aws:secretsmanager:us-west-2:000000000000:secret:test-secret-id-[A-Za-z0-9]{6}";
+ assertSpanClientAttributes(
+ traces,
+ secretsManagerSpanName("DescribeSecret"),
+ getSecretsManagerRpcServiceName(),
+ localService,
+ localOperation,
+ getSecretsManagerServiceName(),
+ "DescribeSecret",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ "localstack",
+ 4566,
+ "http://localstack:4566",
+ 200,
+ List.of(
+ assertAttribute(
+ SemanticConventionsConstants.AWS_SECRET_ARN,
+ "arn:aws:secretsmanager:us-west-2:000000000000:secret:test-secret-id-[A-Za-z0-9]{6}")));
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.LATENCY_METRIC,
+ localService,
+ localOperation,
+ getSecretsManagerServiceName(),
+ "DescribeSecret",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ 5000.0);
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.FAULT_METRIC,
+ localService,
+ localOperation,
+ getSecretsManagerServiceName(),
+ "DescribeSecret",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ 0.0);
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.ERROR_METRIC,
+ localService,
+ localOperation,
+ getSecretsManagerServiceName(),
+ "DescribeSecret",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ 0.0);
+ }
+
+ protected void doTestSecretsManagerError() throws Exception {
+ appClient.get("/secretsmanager/error").aggregate().join();
+ var traces = mockCollectorClient.getTraces();
+ var metrics =
+ mockCollectorClient.getMetrics(
+ Set.of(
+ AppSignalsConstants.ERROR_METRIC,
+ AppSignalsConstants.FAULT_METRIC,
+ AppSignalsConstants.LATENCY_METRIC));
+ var localService = getApplicationOtelServiceName();
+ var localOperation = "GET /secretsmanager/error";
+ assertSpanClientAttributes(
+ traces,
+ secretsManagerSpanName("DescribeSecret"),
+ getSecretsManagerRpcServiceName(),
+ localService,
+ localOperation,
+ getSecretsManagerServiceName(),
+ "DescribeSecret",
+ null,
+ null,
+ null,
+ "error.test",
+ 8080,
+ "http://error.test:8080",
+ 400,
+ List.of());
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.LATENCY_METRIC,
+ localService,
+ localOperation,
+ getSecretsManagerServiceName(),
+ "DescribeSecret",
+ null,
+ null,
+ null,
+ 5000.0);
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.FAULT_METRIC,
+ localService,
+ localOperation,
+ getSecretsManagerServiceName(),
+ "DescribeSecret",
+ null,
+ null,
+ null,
+ 0.0);
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.ERROR_METRIC,
+ localService,
+ localOperation,
+ getSecretsManagerServiceName(),
+ "DescribeSecret",
+ null,
+ null,
+ null,
+ 1.0);
+ }
+
+ protected void doTestSecretsManagerFault() throws Exception {
+ appClient.get("/secretsmanager/fault").aggregate().join();
+ var traces = mockCollectorClient.getTraces();
+ var metrics =
+ mockCollectorClient.getMetrics(
+ Set.of(
+ AppSignalsConstants.ERROR_METRIC,
+ AppSignalsConstants.FAULT_METRIC,
+ AppSignalsConstants.LATENCY_METRIC));
+
+ var localService = getApplicationOtelServiceName();
+ var localOperation = "GET /secretsmanager/fault";
+ assertSpanClientAttributes(
+ traces,
+ secretsManagerSpanName("DescribeSecret"),
+ getSecretsManagerRpcServiceName(),
+ localService,
+ localOperation,
+ getSecretsManagerServiceName(),
+ "DescribeSecret",
+ null,
+ null,
+ null,
+ "fault.test",
+ 8080,
+ "http://fault.test:8080",
+ 500,
+ List.of());
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.LATENCY_METRIC,
+ localService,
+ localOperation,
+ getSecretsManagerServiceName(),
+ "DescribeSecret",
+ null,
+ null,
+ null,
+ 5000.0);
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.FAULT_METRIC,
+ localService,
+ localOperation,
+ getSecretsManagerServiceName(),
+ "DescribeSecret",
+ null,
+ null,
+ null,
+ 1.0);
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.ERROR_METRIC,
+ localService,
+ localOperation,
+ getSecretsManagerServiceName(),
+ "DescribeSecret",
+ null,
+ null,
+ null,
+ 0.0);
+ }
+
+ protected void doTestStepFunctionsDescribeStateMachine() throws Exception {
+ appClient.get("/sfn/describestatemachine/test-state-machine").aggregate().join();
+ var traces = mockCollectorClient.getTraces();
+ var metrics =
+ mockCollectorClient.getMetrics(
+ Set.of(
+ AppSignalsConstants.ERROR_METRIC,
+ AppSignalsConstants.FAULT_METRIC,
+ AppSignalsConstants.LATENCY_METRIC));
+ var localService = getApplicationOtelServiceName();
+ var localOperation = "GET /sfn/describestatemachine/:name";
+ var type = "AWS::StepFunctions::StateMachine";
+ var identifier = "test-state-machine";
+ var cloudformationIdentifier =
+ "arn:aws:states:us-west-2:000000000000:stateMachine:test-state-machine";
+
+ assertSpanClientAttributes(
+ traces,
+ stepFunctionsSpanName("DescribeStateMachine"),
+ getStepFunctionsRpcServiceName(),
+ localService,
+ localOperation,
+ getStepFunctionsServiceName(),
+ "DescribeStateMachine",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ "localstack",
+ 4566,
+ "http://localstack:4566",
+ 200,
+ List.of(
+ assertAttribute(
+ SemanticConventionsConstants.AWS_STATE_MACHINE_ARN,
+ "arn:aws:states:us-west-2:000000000000:stateMachine:test-state-machine")));
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.LATENCY_METRIC,
+ localService,
+ localOperation,
+ getStepFunctionsServiceName(),
+ "DescribeStateMachine",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ 5000.0);
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.FAULT_METRIC,
+ localService,
+ localOperation,
+ getStepFunctionsServiceName(),
+ "DescribeStateMachine",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ 0.0);
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.ERROR_METRIC,
+ localService,
+ localOperation,
+ getStepFunctionsServiceName(),
+ "DescribeStateMachine",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ 0.0);
+ }
+
+ protected void doTestStepFunctionsDescribeActivity() throws Exception {
+ appClient.get("/sfn/describeactivity/test-activity").aggregate().join();
+ var traces = mockCollectorClient.getTraces();
+ var metrics =
+ mockCollectorClient.getMetrics(
+ Set.of(
+ AppSignalsConstants.ERROR_METRIC,
+ AppSignalsConstants.FAULT_METRIC,
+ AppSignalsConstants.LATENCY_METRIC));
+ var localService = getApplicationOtelServiceName();
+ var localOperation = "GET /sfn/describeactivity/:name";
+ var type = "AWS::StepFunctions::Activity";
+ var identifier = "test-activity";
+ var cloudformationIdentifier = "arn:aws:states:us-west-2:000000000000:activity:test-activity";
+
+ assertSpanClientAttributes(
+ traces,
+ stepFunctionsSpanName("DescribeActivity"),
+ getStepFunctionsRpcServiceName(),
+ localService,
+ localOperation,
+ getStepFunctionsServiceName(),
+ "DescribeActivity",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ "localstack",
+ 4566,
+ "http://localstack:4566",
+ 200,
+ List.of(
+ assertAttribute(
+ SemanticConventionsConstants.AWS_ACTIVITY_ARN,
+ "arn:aws:states:us-west-2:000000000000:activity:test-activity")));
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.LATENCY_METRIC,
+ localService,
+ localOperation,
+ getStepFunctionsServiceName(),
+ "DescribeActivity",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ 5000.0);
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.FAULT_METRIC,
+ localService,
+ localOperation,
+ getStepFunctionsServiceName(),
+ "DescribeActivity",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ 0.0);
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.ERROR_METRIC,
+ localService,
+ localOperation,
+ getStepFunctionsServiceName(),
+ "DescribeActivity",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ 0.0);
+ }
+
+ protected void doTestStepFunctionsError() throws Exception {
+ appClient.get("/sfn/error").aggregate().join();
+ var traces = mockCollectorClient.getTraces();
+ var metrics =
+ mockCollectorClient.getMetrics(
+ Set.of(
+ AppSignalsConstants.ERROR_METRIC,
+ AppSignalsConstants.FAULT_METRIC,
+ AppSignalsConstants.LATENCY_METRIC));
+
+ var localService = getApplicationOtelServiceName();
+ var localOperation = "GET /sfn/error";
+ var type = "AWS::StepFunctions::Activity";
+ var identifier = "nonexistent-activity";
+ var cloudformationIdentifier =
+ "arn:aws:states:us-west-2:000000000000:activity:nonexistent-activity";
+
+ assertSpanClientAttributes(
+ traces,
+ stepFunctionsSpanName("DescribeActivity"),
+ getStepFunctionsRpcServiceName(),
+ localService,
+ localOperation,
+ getStepFunctionsServiceName(),
+ "DescribeActivity",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ "error.test",
+ 8080,
+ "http://error.test:8080",
+ 400,
+ List.of(
+ assertAttribute(
+ SemanticConventionsConstants.AWS_ACTIVITY_ARN,
+ "arn:aws:states:us-west-2:000000000000:activity:nonexistent-activity")));
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.LATENCY_METRIC,
+ localService,
+ localOperation,
+ getStepFunctionsServiceName(),
+ "DescribeActivity",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ 5000.0);
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.FAULT_METRIC,
+ localService,
+ localOperation,
+ getStepFunctionsServiceName(),
+ "DescribeActivity",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ 0.0);
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.ERROR_METRIC,
+ localService,
+ localOperation,
+ getStepFunctionsServiceName(),
+ "DescribeActivity",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ 1.0);
+ }
+
+ protected void doTestStepFunctionsFault() throws Exception {
+ appClient.get("/sfn/fault").aggregate().join();
+ var traces = mockCollectorClient.getTraces();
+ var metrics =
+ mockCollectorClient.getMetrics(
+ Set.of(
+ AppSignalsConstants.ERROR_METRIC,
+ AppSignalsConstants.FAULT_METRIC,
+ AppSignalsConstants.LATENCY_METRIC));
+
+ var localService = getApplicationOtelServiceName();
+ var localOperation = "GET /sfn/fault";
+ var type = "AWS::StepFunctions::Activity";
+ var identifier = "fault-activity";
+ var cloudformationIdentifier = "arn:aws:states:us-west-2:000000000000:activity:fault-activity";
+
+ assertSpanClientAttributes(
+ traces,
+ stepFunctionsSpanName("DescribeActivity"),
+ getStepFunctionsRpcServiceName(),
+ localService,
+ localOperation,
+ getStepFunctionsServiceName(),
+ "DescribeActivity",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ "fault.test",
+ 8080,
+ "http://fault.test:8080",
+ 500,
+ List.of(
+ assertAttribute(
+ SemanticConventionsConstants.AWS_ACTIVITY_ARN,
+ "arn:aws:states:us-west-2:000000000000:activity:fault-activity")));
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.LATENCY_METRIC,
+ localService,
+ localOperation,
+ getStepFunctionsServiceName(),
+ "DescribeActivity",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ 5000.0);
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.FAULT_METRIC,
+ localService,
+ localOperation,
+ getStepFunctionsServiceName(),
+ "DescribeActivity",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ 1.0);
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.ERROR_METRIC,
+ localService,
+ localOperation,
+ getStepFunctionsServiceName(),
+ "DescribeActivity",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ 0.0);
+ }
+
+ protected void doTestSnsGetTopicAttributes() throws Exception {
+ appClient.get("/sns/gettopicattributes/test-topic").aggregate().join();
+ var traces = mockCollectorClient.getTraces();
+ var metrics =
+ mockCollectorClient.getMetrics(
+ Set.of(
+ AppSignalsConstants.ERROR_METRIC,
+ AppSignalsConstants.FAULT_METRIC,
+ AppSignalsConstants.LATENCY_METRIC));
+
+ var localService = getApplicationOtelServiceName();
+ var localOperation = "GET /sns/gettopicattributes/:topicId";
+ var type = "AWS::SNS::Topic";
+ var identifier = "test-topic";
+ var cloudformationIdentifier = "arn:aws:sns:us-west-2:000000000000:test-topic";
+
+ assertSpanClientAttributes(
+ traces,
+ snsSpanName("GetTopicAttributes"),
+ getSnsRpcServiceName(),
+ localService,
+ localOperation,
+ getSnsServiceName(),
+ "GetTopicAttributes",
+ type,
+ identifier,
+ cloudformationIdentifier,
+ "localstack",
+ 4566,
+ "http://localstack:4566",
+ 200,
+ List.of(
+ assertAttribute(
+ SemanticConventionsConstants.AWS_TOPIC_ARN,
+ "arn:aws:sns:us-west-2:000000000000:test-topic")));
+ }
+
+ protected void doTestSnsError() throws Exception {
+ appClient.get("/sns/error").aggregate().join();
+ var traces = mockCollectorClient.getTraces();
+ var metrics =
+ mockCollectorClient.getMetrics(
+ Set.of(
+ AppSignalsConstants.ERROR_METRIC,
+ AppSignalsConstants.FAULT_METRIC,
+ AppSignalsConstants.LATENCY_METRIC));
+
+ var localService = getApplicationOtelServiceName();
+ var localOperation = "GET /sns/error";
+ assertSpanClientAttributes(
+ traces,
+ snsSpanName("GetTopicAttributes"),
+ getSnsRpcServiceName(),
+ localService,
+ localOperation,
+ getSnsServiceName(),
+ "GetTopicAttributes",
+ null,
+ null,
+ null,
+ "error.test",
+ 8080,
+ "http://error.test:8080",
+ 400,
+ List.of());
+
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.LATENCY_METRIC,
+ localService,
+ localOperation,
+ getSnsServiceName(),
+ "GetTopicAttributes",
+ null,
+ null,
+ null,
+ 5000.0);
+
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.FAULT_METRIC,
+ localService,
+ localOperation,
+ getSnsServiceName(),
+ "GetTopicAttributes",
+ null,
+ null,
+ null,
+ 0.0);
+
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.ERROR_METRIC,
+ localService,
+ localOperation,
+ getSnsServiceName(),
+ "GetTopicAttributes",
+ null,
+ null,
+ null,
+ 1.0);
+ }
+
+ protected void doTestSnsFault() throws Exception {
+ appClient.get("/sns/fault").aggregate().join();
+ var traces = mockCollectorClient.getTraces();
+ var metrics =
+ mockCollectorClient.getMetrics(
+ Set.of(
+ AppSignalsConstants.ERROR_METRIC,
+ AppSignalsConstants.FAULT_METRIC,
+ AppSignalsConstants.LATENCY_METRIC));
+
+ var localService = getApplicationOtelServiceName();
+ var localOperation = "GET /sns/fault";
+ assertSpanClientAttributes(
+ traces,
+ snsSpanName("GetTopicAttributes"),
+ getSnsRpcServiceName(),
+ localService,
+ localOperation,
+ getSnsServiceName(),
+ "GetTopicAttributes",
+ null,
+ null,
+ null,
+ "fault.test",
+ 8080,
+ "http://fault.test:8080",
+ 500,
+ List.of());
+
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.LATENCY_METRIC,
+ localService,
+ localOperation,
+ getSnsServiceName(),
+ "GetTopicAttributes",
+ null,
+ null,
+ null,
+ 5000.0);
+
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.FAULT_METRIC,
+ localService,
+ localOperation,
+ getSnsServiceName(),
+ "GetTopicAttributes",
+ null,
+ null,
+ null,
+ 1.0);
+
+ assertMetricClientAttributes(
+ metrics,
+ AppSignalsConstants.ERROR_METRIC,
+ localService,
+ localOperation,
+ getSnsServiceName(),
+ "GetTopicAttributes",
+ null,
+ null,
+ null,
0.0);
}
}
diff --git a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v1/AwsSdkV1Test.java b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v1/AwsSdkV1Test.java
index fa5a586c5d..5a53b83a1e 100644
--- a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v1/AwsSdkV1Test.java
+++ b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v1/AwsSdkV1Test.java
@@ -76,6 +76,21 @@ protected String getBedrockAgentRuntimeSpanNamePrefix() {
return "AWSBedrockAgentRuntime";
}
+ @Override
+ protected String getSecretsManagerSpanNamePrefix() {
+ return "AWSSecretsManager";
+ }
+
+ @Override
+ protected String getStepFunctionsSpanNamePrefix() {
+ return "AWSStepFunctions";
+ }
+
+ @Override
+ protected String getSnsSpanNamePrefix() {
+ return "SNS";
+ }
+
protected String getS3RpcServiceName() {
return "Amazon S3";
}
@@ -90,6 +105,21 @@ protected String getSqsRpcServiceName() {
return "AmazonSQS";
}
+ @Override
+ protected String getSecretsManagerRpcServiceName() {
+ return "AWSSecretsManager";
+ }
+
+ @Override
+ protected String getStepFunctionsRpcServiceName() {
+ return "AWSStepFunctions";
+ }
+
+ @Override
+ protected String getSnsRpcServiceName() {
+ return "AmazonSNS";
+ }
+
protected String getKinesisRpcServiceName() {
return "AmazonKinesis";
}
@@ -260,4 +290,54 @@ void testBedrockAgentRuntimeAgentId() {
void testBedrockAgentRuntimeKnowledgeBaseId() {
doTestBedrockAgentRuntimeKnowledgeBaseId();
}
+
+ @Test
+ void testSecretsManagerDescribeSecret() throws Exception {
+ doTestSecretsManagerDescribeSecret();
+ }
+
+ @Test
+ void testSecretsManagerError() throws Exception {
+ doTestSecretsManagerError();
+ }
+
+ @Test
+ void testSecretsManagerFault() throws Exception {
+ doTestSecretsManagerFault();
+ }
+
+ @Test
+ void testStepFunctionsDescribeStateMachine() throws Exception {
+ doTestStepFunctionsDescribeStateMachine();
+ }
+
+ @Test
+ void testStepFunctionsDescribeActivity() throws Exception {
+ doTestStepFunctionsDescribeActivity();
+ }
+
+ @Test
+ void testStepFunctionsError() throws Exception {
+ doTestStepFunctionsError();
+ }
+
+ @Test
+ void testStepFunctionsFault() throws Exception {
+ doTestStepFunctionsFault();
+ }
+
+ @Test
+ void testSnsGetTopicAttributes() throws Exception {
+ doTestSnsGetTopicAttributes();
+ }
+
+ @Test
+ void testSnsError() throws Exception {
+ doTestStepFunctionsError();
+ }
+
+ @Test
+ void testSnsFault() throws Exception {
+ doTestStepFunctionsFault();
+ }
}
diff --git a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v2/AwsSdkV2Test.java b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v2/AwsSdkV2Test.java
index 46c6b7e425..c1259ca6cc 100644
--- a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v2/AwsSdkV2Test.java
+++ b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/awssdk/v2/AwsSdkV2Test.java
@@ -75,6 +75,21 @@ protected String getBedrockAgentRuntimeSpanNamePrefix() {
return "BedrockAgentRuntime";
}
+ @Override
+ protected String getSecretsManagerSpanNamePrefix() {
+ return "SecretsManager";
+ }
+
+ @Override
+ protected String getStepFunctionsSpanNamePrefix() {
+ return "Sfn";
+ }
+
+ @Override
+ protected String getSnsSpanNamePrefix() {
+ return "Sns";
+ }
+
@Override
protected String getS3RpcServiceName() {
return "S3";
@@ -114,6 +129,21 @@ protected String getBedrockAgentRuntimeRpcServiceName() {
return "BedrockAgentRuntime";
}
+ @Override
+ protected String getSecretsManagerRpcServiceName() {
+ return "SecretsManager";
+ }
+
+ @Override
+ protected String getStepFunctionsRpcServiceName() {
+ return "Sfn";
+ }
+
+ @Override
+ protected String getSnsRpcServiceName() {
+ return "Sns";
+ }
+
@Test
void testS3CreateBucket() throws Exception {
doTestS3CreateBucket();
@@ -259,10 +289,58 @@ void testBedrockAgentRuntimeAgentId() {
doTestBedrockAgentRuntimeAgentId();
}
- // TODO: Enable testBedrockAgentRuntimeKnowledgeBaseId test after KnowledgeBaseId is supported in
- // OTEL BedrockAgentRuntime instrumentation
@Test
void testBedrockAgentRuntimeKnowledgeBaseId() {
doTestBedrockAgentRuntimeKnowledgeBaseId();
}
+
+ @Test
+ void testSecretsManagerDescribeSecret() throws Exception {
+ doTestSecretsManagerDescribeSecret();
+ }
+
+ @Test
+ void testSecretsManagerError() throws Exception {
+ doTestSecretsManagerError();
+ }
+
+ @Test
+ void testSecretsManagerFault() throws Exception {
+ doTestSecretsManagerFault();
+ }
+
+ @Test
+ void testStepFunctionsDescribeStateMachine() throws Exception {
+ doTestStepFunctionsDescribeStateMachine();
+ }
+
+ @Test
+ void testStepFunctionsDescribeActivity() throws Exception {
+ doTestStepFunctionsDescribeActivity();
+ }
+
+ @Test
+ void testStepFunctionsError() throws Exception {
+ doTestStepFunctionsError();
+ }
+
+ @Test
+ void testStepFunctionsFault() throws Exception {
+ doTestStepFunctionsFault();
+ }
+
+ @Test
+ void testSnsGetTopicAttributes() throws Exception {
+ doTestSnsGetTopicAttributes();
+ }
+
+ @Test
+ void testSnsError() throws Exception {
+ doTestStepFunctionsError();
+ }
+
+ @Test
+ void testSnsFault() throws Exception {
+ doTestStepFunctionsFault();
+ }
}
diff --git a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/AppSignalsConstants.java b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/AppSignalsConstants.java
index 675b69032b..0ff11305c2 100644
--- a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/AppSignalsConstants.java
+++ b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/AppSignalsConstants.java
@@ -31,6 +31,8 @@ public class AppSignalsConstants {
public static final String AWS_REMOTE_OPERATION = "aws.remote.operation";
public static final String AWS_REMOTE_RESOURCE_TYPE = "aws.remote.resource.type";
public static final String AWS_REMOTE_RESOURCE_IDENTIFIER = "aws.remote.resource.identifier";
+ public static final String AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER =
+ "aws.remote.resource.cfn.primary.identifier";
public static final String AWS_SPAN_KIND = "aws.span.kind";
public static final String AWS_REMOTE_DB_USER = "aws.remote.db.user";
diff --git a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/SemanticConventionsConstants.java b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/SemanticConventionsConstants.java
index f0cac6da46..51077ea6a1 100644
--- a/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/SemanticConventionsConstants.java
+++ b/appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/SemanticConventionsConstants.java
@@ -62,6 +62,7 @@ public class SemanticConventionsConstants {
public static final String AWS_DATA_SOURCE_ID = "aws.bedrock.data_source.id";
public static final String AWS_AGENT_ID = "aws.bedrock.agent.id";
public static final String AWS_GUARDRAIL_ID = "aws.bedrock.guardrail.id";
+ public static final String AWS_GUARDRAIL_ARN = "aws.bedrock.guardrail.arn";
public static final String GEN_AI_REQUEST_MODEL = "gen_ai.request.model";
public static final String GEN_AI_REQUEST_MAX_TOKENS = "gen_ai.request.max_tokens";
public static final String GEN_AI_REQUEST_TEMPERATURE = "gen_ai.request.temperature";
@@ -69,6 +70,10 @@ public class SemanticConventionsConstants {
public static final String GEN_AI_RESPONSE_FINISH_REASONS = "gen_ai.response.finish_reasons";
public static final String GEN_AI_USAGE_INPUT_TOKENS = "gen_ai.usage.input_tokens";
public static final String GEN_AI_USAGE_OUTPUT_TOKENS = "gen_ai.usage.output_tokens";
+ public static final String AWS_SECRET_ARN = "aws.secretsmanager.secret.arn";
+ public static final String AWS_STATE_MACHINE_ARN = "aws.stepfunctions.state_machine.arn";
+ public static final String AWS_ACTIVITY_ARN = "aws.stepfunctions.activity.arn";
+ public static final String AWS_TOPIC_ARN = "aws.sns.topic.arn";
// kafka
public static final String MESSAGING_CLIENT_ID = "messaging.client_id";
diff --git a/appsignals-tests/images/aws-sdk/aws-sdk-v1/build.gradle.kts b/appsignals-tests/images/aws-sdk/aws-sdk-v1/build.gradle.kts
index 6ee3f0cae1..77fad5427c 100644
--- a/appsignals-tests/images/aws-sdk/aws-sdk-v1/build.gradle.kts
+++ b/appsignals-tests/images/aws-sdk/aws-sdk-v1/build.gradle.kts
@@ -33,6 +33,10 @@ dependencies {
implementation("com.amazonaws:aws-java-sdk-dynamodb")
implementation("com.amazonaws:aws-java-sdk-sqs")
implementation("com.amazonaws:aws-java-sdk-kinesis")
+ implementation("com.amazonaws:aws-java-sdk-secretsmanager")
+ implementation("com.amazonaws:aws-java-sdk-iam")
+ implementation("com.amazonaws:aws-java-sdk-stepfunctions")
+ implementation("com.amazonaws:aws-java-sdk-sns")
implementation("com.amazonaws:aws-java-sdk-bedrock")
implementation("com.amazonaws:aws-java-sdk-bedrockagent")
implementation("com.amazonaws:aws-java-sdk-bedrockruntime")
diff --git a/appsignals-tests/images/aws-sdk/aws-sdk-v1/src/main/java/com/amazon/sampleapp/App.java b/appsignals-tests/images/aws-sdk/aws-sdk-v1/src/main/java/com/amazon/sampleapp/App.java
index ad5f7a73b4..6b39559b0d 100644
--- a/appsignals-tests/images/aws-sdk/aws-sdk-v1/src/main/java/com/amazon/sampleapp/App.java
+++ b/appsignals-tests/images/aws-sdk/aws-sdk-v1/src/main/java/com/amazon/sampleapp/App.java
@@ -44,6 +44,9 @@
import com.amazonaws.services.dynamodbv2.model.KeyType;
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
import com.amazonaws.services.dynamodbv2.model.PutItemRequest;
+import com.amazonaws.services.identitymanagement.AmazonIdentityManagementClient;
+import com.amazonaws.services.identitymanagement.model.CreateRoleRequest;
+import com.amazonaws.services.identitymanagement.model.PutRolePolicyRequest;
import com.amazonaws.services.kinesis.AmazonKinesisClient;
import com.amazonaws.services.kinesis.model.CreateStreamRequest;
import com.amazonaws.services.kinesis.model.PutRecordRequest;
@@ -52,10 +55,30 @@
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.Region;
+import com.amazonaws.services.secretsmanager.AWSSecretsManagerClient;
+import com.amazonaws.services.secretsmanager.model.CreateSecretRequest;
+import com.amazonaws.services.secretsmanager.model.DescribeSecretRequest;
+import com.amazonaws.services.secretsmanager.model.ListSecretsRequest;
+import com.amazonaws.services.secretsmanager.model.SecretListEntry;
+import com.amazonaws.services.sns.AmazonSNSClient;
+import com.amazonaws.services.sns.model.CreateTopicRequest;
+import com.amazonaws.services.sns.model.GetTopicAttributesRequest;
+import com.amazonaws.services.sns.model.ListTopicsRequest;
+import com.amazonaws.services.sns.model.Topic;
import com.amazonaws.services.sqs.AmazonSQSClient;
import com.amazonaws.services.sqs.model.CreateQueueRequest;
import com.amazonaws.services.sqs.model.ReceiveMessageRequest;
import com.amazonaws.services.sqs.model.SendMessageRequest;
+import com.amazonaws.services.stepfunctions.AWSStepFunctionsClient;
+import com.amazonaws.services.stepfunctions.model.ActivityListItem;
+import com.amazonaws.services.stepfunctions.model.CreateActivityRequest;
+import com.amazonaws.services.stepfunctions.model.CreateStateMachineRequest;
+import com.amazonaws.services.stepfunctions.model.DescribeActivityRequest;
+import com.amazonaws.services.stepfunctions.model.DescribeStateMachineRequest;
+import com.amazonaws.services.stepfunctions.model.ListActivitiesRequest;
+import com.amazonaws.services.stepfunctions.model.ListStateMachinesRequest;
+import com.amazonaws.services.stepfunctions.model.StateMachineListItem;
+import com.amazonaws.services.stepfunctions.model.StateMachineType;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.IOException;
@@ -125,6 +148,9 @@ public static void main(String[] args) throws IOException, InterruptedException
setupS3();
setupSqs();
setupKinesis();
+ setupSecretsManager();
+ setupStepFunctions();
+ setupSns();
setupBedrock();
// Add this log line so that we only start testing after all routes are configured.
@@ -518,6 +544,367 @@ private static void setupS3() {
});
}
+ private static void setupSecretsManager() {
+ var secretsManagerClient =
+ AWSSecretsManagerClient.builder()
+ .withCredentials(CREDENTIALS_PROVIDER)
+ .withEndpointConfiguration(endpointConfiguration)
+ .build();
+ var secretName = "test-secret-id";
+ String existingSecretArn = null;
+ try {
+ var listRequest = new ListSecretsRequest();
+ var listResponse = secretsManagerClient.listSecrets(listRequest);
+ existingSecretArn =
+ listResponse.getSecretList().stream()
+ .filter(secret -> secret.getName().contains(secretName))
+ .findFirst()
+ .map(SecretListEntry::getARN)
+ .orElse(null);
+ } catch (Exception e) {
+ logger.error("Error listing secrets", e);
+ }
+
+ if (existingSecretArn != null) {
+ logger.debug("Secret already exists, skipping creation");
+ } else {
+ logger.info("Secret not found, creating new one");
+ var createSecretRequest = new CreateSecretRequest().withName(secretName);
+ var createSecretResponse = secretsManagerClient.createSecret(createSecretRequest);
+ existingSecretArn = createSecretResponse.getARN();
+ }
+
+ String finalExistingSecretArn = existingSecretArn;
+ get(
+ "/secretsmanager/describesecret/:secretId",
+ (req, res) -> {
+ var describeRequest = new DescribeSecretRequest().withSecretId(finalExistingSecretArn);
+ secretsManagerClient.describeSecret(describeRequest);
+ return "";
+ });
+
+ get(
+ "/secretsmanager/error",
+ (req, res) -> {
+ setMainStatus(400);
+ var errorClient =
+ AWSSecretsManagerClient.builder()
+ .withCredentials(CREDENTIALS_PROVIDER)
+ .withEndpointConfiguration(
+ new EndpointConfiguration(
+ "http://error.test:8080", Regions.US_WEST_2.getName()))
+ .build();
+
+ try {
+ var describeRequest =
+ new DescribeSecretRequest()
+ .withSecretId(
+ "arn:aws:secretsmanager:us-west-2:000000000000:secret:nonexistent-secret-id");
+ errorClient.describeSecret(describeRequest);
+ } catch (Exception e) {
+ logger.debug("Error describing secret", e);
+ }
+ return "";
+ });
+
+ get(
+ "/secretsmanager/fault",
+ (req, res) -> {
+ setMainStatus(500);
+ var faultClient =
+ AWSSecretsManagerClient.builder()
+ .withCredentials(CREDENTIALS_PROVIDER)
+ .withEndpointConfiguration(
+ new EndpointConfiguration(
+ "http://fault.test:8080", Regions.US_WEST_2.getName()))
+ .build();
+
+ try {
+ var describeRequest =
+ new DescribeSecretRequest()
+ .withSecretId(
+ "arn:aws:secretsmanager:us-west-2:000000000000:secret:fault-secret-id");
+ faultClient.describeSecret(describeRequest);
+ } catch (Exception e) {
+ logger.debug("Error describing secret", e);
+ }
+ return "";
+ });
+ }
+
+ private static void setupStepFunctions() {
+ var stepFunctionsClient =
+ AWSStepFunctionsClient.builder()
+ .withCredentials(CREDENTIALS_PROVIDER)
+ .withEndpointConfiguration(endpointConfiguration)
+ .build();
+ var iamClient =
+ AmazonIdentityManagementClient.builder()
+ .withCredentials(CREDENTIALS_PROVIDER)
+ .withEndpointConfiguration(endpointConfiguration)
+ .build();
+
+ var sfnName = "test-state-machine";
+ String existingStateMachineArn = null;
+ try {
+ var listRequest = new ListStateMachinesRequest();
+ var listResponse = stepFunctionsClient.listStateMachines(listRequest);
+ existingStateMachineArn =
+ listResponse.getStateMachines().stream()
+ .filter(machine -> machine.getName().equals(sfnName))
+ .findFirst()
+ .map(StateMachineListItem::getStateMachineArn)
+ .orElse(null);
+ } catch (Exception e) {
+ logger.error("Error listing state machines", e);
+ }
+
+ if (existingStateMachineArn != null) {
+ logger.debug("State machine already exists, skipping creation");
+ } else {
+ logger.debug("State machine not found, creating new one");
+ String trustPolicy =
+ "{"
+ + "\"Version\": \"2012-10-17\","
+ + "\"Statement\": ["
+ + " {"
+ + " \"Effect\": \"Allow\","
+ + " \"Principal\": {"
+ + " \"Service\": \"states.amazonaws.com\""
+ + " },"
+ + " \"Action\": \"sts:AssumeRole\""
+ + " }"
+ + "]}";
+ var roleRequest =
+ new CreateRoleRequest()
+ .withRoleName(sfnName + "-role")
+ .withAssumeRolePolicyDocument(trustPolicy);
+ var roleArn = iamClient.createRole(roleRequest).getRole().getArn();
+ String policyDocument =
+ "{"
+ + "\"Version\": \"2012-10-17\","
+ + "\"Statement\": ["
+ + " {"
+ + " \"Effect\": \"Allow\","
+ + " \"Action\": ["
+ + " \"lambda:InvokeFunction\""
+ + " ],"
+ + " \"Resource\": ["
+ + " \"*\""
+ + " ]"
+ + " }"
+ + "]}";
+ var policyRequest =
+ new PutRolePolicyRequest()
+ .withRoleName(sfnName + "-role")
+ .withPolicyName(sfnName + "-policy")
+ .withPolicyDocument(policyDocument);
+ iamClient.putRolePolicy(policyRequest);
+ String stateMachineDefinition =
+ "{"
+ + " \"Comment\": \"A Hello World example of the Amazon States Language using a Pass state\","
+ + " \"StartAt\": \"HelloWorld\","
+ + " \"States\": {"
+ + " \"HelloWorld\": {"
+ + " \"Type\": \"Pass\","
+ + " \"Result\": \"Hello World!\","
+ + " \"End\": true"
+ + " }"
+ + " }"
+ + "}";
+ var sfnRequest =
+ new CreateStateMachineRequest()
+ .withName(sfnName)
+ .withRoleArn(roleArn)
+ .withDefinition(stateMachineDefinition)
+ .withType(StateMachineType.STANDARD);
+ var createResponse = stepFunctionsClient.createStateMachine(sfnRequest);
+ existingStateMachineArn = createResponse.getStateMachineArn();
+ }
+
+ var activityName = "test-activity";
+ String existingActivityArn = null;
+ try {
+ var listRequest = new ListActivitiesRequest();
+ var listResponse = stepFunctionsClient.listActivities(listRequest);
+ existingActivityArn =
+ listResponse.getActivities().stream()
+ .filter(activity -> activity.getName().equals(activityName))
+ .findFirst()
+ .map(ActivityListItem::getActivityArn)
+ .orElse(null);
+ } catch (Exception e) {
+ logger.error("Error listing activities", e);
+ }
+
+ if (existingActivityArn != null) {
+ logger.debug("Activity already exists, skipping creation");
+ } else {
+ logger.debug("Activity does not exist, creating new one");
+ var createRequest = new CreateActivityRequest().withName(activityName);
+ var createResponse = stepFunctionsClient.createActivity(createRequest);
+ existingActivityArn = createResponse.getActivityArn();
+ }
+
+ String finalExistingStateMachineArn = existingStateMachineArn;
+ String finalExistingActivityArn = existingActivityArn;
+
+ get(
+ "/sfn/describestatemachine/:name",
+ (req, res) -> {
+ var describeRequest =
+ new DescribeStateMachineRequest().withStateMachineArn(finalExistingStateMachineArn);
+ stepFunctionsClient.describeStateMachine(describeRequest);
+ return "";
+ });
+
+ get(
+ "/sfn/describeactivity/:name",
+ (req, res) -> {
+ var describeRequest =
+ new DescribeActivityRequest().withActivityArn(finalExistingActivityArn);
+ stepFunctionsClient.describeActivity(describeRequest);
+ return "";
+ });
+
+ get(
+ "/sfn/error",
+ (req, res) -> {
+ setMainStatus(400);
+ var errorClient =
+ AWSStepFunctionsClient.builder()
+ .withCredentials(CREDENTIALS_PROVIDER)
+ .withEndpointConfiguration(
+ new EndpointConfiguration(
+ "http://error.test:8080", Regions.US_WEST_2.getName()))
+ .build();
+
+ try {
+ var describeRequest =
+ new DescribeActivityRequest()
+ .withActivityArn(
+ "arn:aws:states:us-west-2:000000000000:activity:nonexistent-activity");
+ errorClient.describeActivity(describeRequest);
+ } catch (Exception e) {
+ logger.error("Error describing activity", e);
+ }
+ return "";
+ });
+
+ get(
+ "/sfn/fault",
+ (req, res) -> {
+ setMainStatus(500);
+ var faultClient =
+ AWSStepFunctionsClient.builder()
+ .withCredentials(CREDENTIALS_PROVIDER)
+ .withEndpointConfiguration(
+ new EndpointConfiguration(
+ "http://fault.test:8080", Regions.US_WEST_2.getName()))
+ .build();
+
+ try {
+ var describeRequest =
+ new DescribeActivityRequest()
+ .withActivityArn(
+ "arn:aws:states:us-west-2:000000000000:activity:fault-activity");
+ faultClient.describeActivity(describeRequest);
+ } catch (Exception e) {
+ logger.error("Error describing activity", e);
+ }
+ return "";
+ });
+ }
+
+ private static void setupSns() {
+ var snsClient =
+ AmazonSNSClient.builder()
+ .withCredentials(CREDENTIALS_PROVIDER)
+ .withEndpointConfiguration(endpointConfiguration)
+ .build();
+
+ var topicName = "test-topic";
+ String existingTopicArn = null;
+
+ try {
+ var listTopicsRequest = new ListTopicsRequest();
+ var listTopicsResult = snsClient.listTopics(listTopicsRequest);
+ existingTopicArn =
+ listTopicsResult.getTopics().stream()
+ .filter(topic -> topic.getTopicArn().contains(topicName))
+ .findFirst()
+ .map(Topic::getTopicArn)
+ .orElse(null);
+ } catch (Exception e) {
+ logger.error("Error listing topics", e);
+ }
+
+ if (existingTopicArn != null) {
+ logger.debug("Topic already exists, skipping creation");
+ } else {
+ logger.debug("Topic does not exist, creating new one");
+ var createTopicRequest = new CreateTopicRequest().withName(topicName);
+ var createTopicResult = snsClient.createTopic(createTopicRequest);
+ existingTopicArn = createTopicResult.getTopicArn();
+ }
+
+ String finalExistingTopicArn = existingTopicArn;
+ get(
+ "/sns/gettopicattributes/:topicId",
+ (req, res) -> {
+ var getTopicAttributesRequest =
+ new GetTopicAttributesRequest().withTopicArn(finalExistingTopicArn);
+ snsClient.getTopicAttributes(getTopicAttributesRequest);
+ return "";
+ });
+
+ get(
+ "/sns/error",
+ (req, res) -> {
+ setMainStatus(400);
+ var errorClient =
+ AmazonSNSClient.builder()
+ .withCredentials(CREDENTIALS_PROVIDER)
+ .withEndpointConfiguration(
+ new EndpointConfiguration(
+ "https://error.test:8080", Regions.US_WEST_2.getName()))
+ .build();
+
+ try {
+ var getTopicAttributesRequest =
+ new GetTopicAttributesRequest()
+ .withTopicArn("arn:aws:sns:us-west-2:000000000000:nonexistent-topic");
+ errorClient.getTopicAttributes(getTopicAttributesRequest);
+ } catch (Exception e) {
+ logger.error("Error describing topic", e);
+ }
+ return "";
+ });
+
+ get(
+ "/sns/fault",
+ (req, res) -> {
+ setMainStatus(500);
+ var faultClient =
+ AmazonSNSClient.builder()
+ .withCredentials(CREDENTIALS_PROVIDER)
+ .withEndpointConfiguration(
+ new EndpointConfiguration(
+ "http://fault.test:8080", Regions.US_WEST_2.getName()))
+ .build();
+
+ try {
+ var getTopicAttributesRequest =
+ new GetTopicAttributesRequest()
+ .withTopicArn("arn:aws:sns:us-west-2:000000000000:fault-topic");
+ faultClient.getTopicAttributes(getTopicAttributesRequest);
+ } catch (Exception e) {
+ logger.error("Error describing topic", e);
+ }
+ return "";
+ });
+ }
+
private static void setupBedrock() {
// Localstack does not support Bedrock related services.
// We point all Bedrock related request endpoints to the local app,
diff --git a/appsignals-tests/images/aws-sdk/aws-sdk-v2/build.gradle.kts b/appsignals-tests/images/aws-sdk/aws-sdk-v2/build.gradle.kts
index e50f70772f..0ba17450d6 100644
--- a/appsignals-tests/images/aws-sdk/aws-sdk-v2/build.gradle.kts
+++ b/appsignals-tests/images/aws-sdk/aws-sdk-v2/build.gradle.kts
@@ -33,6 +33,10 @@ dependencies {
implementation("software.amazon.awssdk:dynamodb")
implementation("software.amazon.awssdk:sqs")
implementation("software.amazon.awssdk:kinesis")
+ implementation("software.amazon.awssdk:secretsmanager")
+ implementation("software.amazon.awssdk:iam")
+ implementation("software.amazon.awssdk:sfn")
+ implementation("software.amazon.awssdk:sns")
implementation("software.amazon.awssdk:bedrock")
implementation("software.amazon.awssdk:bedrockagent")
implementation("software.amazon.awssdk:bedrockruntime")
diff --git a/appsignals-tests/images/aws-sdk/aws-sdk-v2/src/main/java/com/amazon/sampleapp/App.java b/appsignals-tests/images/aws-sdk/aws-sdk-v2/src/main/java/com/amazon/sampleapp/App.java
index 2982135fd4..c96b762dfd 100644
--- a/appsignals-tests/images/aws-sdk/aws-sdk-v2/src/main/java/com/amazon/sampleapp/App.java
+++ b/appsignals-tests/images/aws-sdk/aws-sdk-v2/src/main/java/com/amazon/sampleapp/App.java
@@ -60,6 +60,9 @@
import software.amazon.awssdk.services.dynamodb.model.KeyType;
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
+import software.amazon.awssdk.services.iam.IamClient;
+import software.amazon.awssdk.services.iam.model.CreateRoleRequest;
+import software.amazon.awssdk.services.iam.model.PutRolePolicyRequest;
import software.amazon.awssdk.services.kinesis.KinesisClient;
import software.amazon.awssdk.services.kinesis.model.CreateStreamRequest;
import software.amazon.awssdk.services.kinesis.model.DescribeStreamRequest;
@@ -68,6 +71,26 @@
import software.amazon.awssdk.services.s3.model.CreateBucketRequest;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
+import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
+import software.amazon.awssdk.services.secretsmanager.model.CreateSecretRequest;
+import software.amazon.awssdk.services.secretsmanager.model.DescribeSecretRequest;
+import software.amazon.awssdk.services.secretsmanager.model.ListSecretsRequest;
+import software.amazon.awssdk.services.secretsmanager.model.SecretListEntry;
+import software.amazon.awssdk.services.sfn.SfnClient;
+import software.amazon.awssdk.services.sfn.model.ActivityListItem;
+import software.amazon.awssdk.services.sfn.model.CreateActivityRequest;
+import software.amazon.awssdk.services.sfn.model.CreateStateMachineRequest;
+import software.amazon.awssdk.services.sfn.model.DescribeActivityRequest;
+import software.amazon.awssdk.services.sfn.model.DescribeStateMachineRequest;
+import software.amazon.awssdk.services.sfn.model.ListActivitiesRequest;
+import software.amazon.awssdk.services.sfn.model.ListStateMachinesRequest;
+import software.amazon.awssdk.services.sfn.model.StateMachineListItem;
+import software.amazon.awssdk.services.sfn.model.StateMachineType;
+import software.amazon.awssdk.services.sns.SnsClient;
+import software.amazon.awssdk.services.sns.model.CreateTopicRequest;
+import software.amazon.awssdk.services.sns.model.GetTopicAttributesRequest;
+import software.amazon.awssdk.services.sns.model.ListTopicsRequest;
+import software.amazon.awssdk.services.sns.model.Topic;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.CreateQueueRequest;
import software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest;
@@ -121,7 +144,10 @@ public static void main(String[] args) throws IOException, InterruptedException
setupS3();
setupSqs();
setupKinesis();
+ setupSecretsManager();
+ setupSfn();
setupBedrock();
+ setupSns();
// Add this log line so that we only start testing after all routes are configured.
awaitInitialization();
logger.info("All routes initialized");
@@ -532,6 +558,365 @@ private static void setupS3() {
});
}
+ private static void setupSecretsManager() {
+ var secretsManagerClient =
+ SecretsManagerClient.builder()
+ .endpointOverride(endpoint)
+ .credentialsProvider(CREDENTIALS_PROVIDER)
+ .build();
+ var secretName = "test-secret-id";
+ String existingSecretArn = null;
+ try {
+ var listRequest = ListSecretsRequest.builder().build();
+ var listResponse = secretsManagerClient.listSecrets(listRequest);
+ existingSecretArn =
+ listResponse.secretList().stream()
+ .filter(secret -> secret.name().contains(secretName))
+ .findFirst()
+ .map(SecretListEntry::arn)
+ .orElse(null);
+ } catch (Exception e) {
+ logger.error("Error listing secrets", e);
+ }
+
+ if (existingSecretArn != null) {
+ logger.debug("Secret already exists, skipping creation");
+ } else {
+ logger.debug("Secret not found, creating a new one");
+ var createSecretRequest = CreateSecretRequest.builder().name(secretName).build();
+ var createSecretResponse = secretsManagerClient.createSecret(createSecretRequest);
+ existingSecretArn = createSecretResponse.arn();
+ }
+
+ String finalExistingSecretArn = existingSecretArn;
+ get(
+ "/secretsmanager/describesecret/:secretId",
+ (req, res) -> {
+ var describeRequest =
+ DescribeSecretRequest.builder().secretId(finalExistingSecretArn).build();
+ secretsManagerClient.describeSecret(describeRequest);
+ return "";
+ });
+
+ get(
+ "/secretsmanager/error",
+ (req, res) -> {
+ setMainStatus(400);
+ var errorClient =
+ SecretsManagerClient.builder()
+ .endpointOverride(URI.create("http://error.test:8080"))
+ .credentialsProvider(CREDENTIALS_PROVIDER)
+ .build();
+
+ try {
+ var describeRequest =
+ DescribeSecretRequest.builder()
+ .secretId(
+ "arn:aws:secretsmanager:us-west-2:000000000000:secret:nonexistent-secret-id")
+ .build();
+ errorClient.describeSecret(describeRequest);
+ } catch (Exception e) {
+ logger.error("Error describing secret", e);
+ }
+ return "";
+ });
+
+ get(
+ "/secretsmanager/fault",
+ (req, res) -> {
+ setMainStatus(500);
+ var faultClient =
+ SecretsManagerClient.builder()
+ .endpointOverride(URI.create("http://fault.test:8080"))
+ .credentialsProvider(CREDENTIALS_PROVIDER)
+ .build();
+
+ try {
+ var describeRequest =
+ DescribeSecretRequest.builder()
+ .secretId(
+ "arn:aws:secretsmanager:us-west-2:000000000000:secret:fault-secret-id")
+ .build();
+ faultClient.describeSecret(describeRequest);
+ } catch (Exception e) {
+ logger.error("Error describing secret", e);
+ }
+ return "";
+ });
+ }
+
+ private static void setupSfn() {
+ var sfnClient =
+ SfnClient.builder()
+ .endpointOverride(endpoint)
+ .credentialsProvider(CREDENTIALS_PROVIDER)
+ .build();
+ var iamClient =
+ IamClient.builder()
+ .endpointOverride(endpoint)
+ .credentialsProvider(CREDENTIALS_PROVIDER)
+ .build();
+
+ var sfnName = "test-state-machine";
+ String existingStateMachineArn = null;
+ try {
+ var listRequest = ListStateMachinesRequest.builder().build();
+ var listResponse = sfnClient.listStateMachines(listRequest);
+ existingStateMachineArn =
+ listResponse.stateMachines().stream()
+ .filter(machine -> machine.name().equals(sfnName))
+ .findFirst()
+ .map(StateMachineListItem::stateMachineArn)
+ .orElse(null);
+ } catch (Exception e) {
+ logger.error("Error listing state machines", e);
+ }
+
+ if (existingStateMachineArn != null) {
+ logger.debug("State machine already exists, skipping creation");
+ } else {
+ logger.debug("State machine not found, creating a new one");
+ String trustPolicy =
+ "{"
+ + "\"Version\": \"2012-10-17\","
+ + "\"Statement\": ["
+ + " {"
+ + " \"Effect\": \"Allow\","
+ + " \"Principal\": {"
+ + " \"Service\": \"states.amazonaws.com\""
+ + " },"
+ + " \"Action\": \"sts:AssumeRole\""
+ + " }"
+ + "]}";
+ var roleRequest =
+ CreateRoleRequest.builder()
+ .roleName(sfnName + "-role")
+ .assumeRolePolicyDocument(trustPolicy)
+ .build();
+ var roleArn = iamClient.createRole(roleRequest).role().arn();
+ String policyDocument =
+ "{"
+ + "\"Version\": \"2012-10-17\","
+ + "\"Statement\": ["
+ + " {"
+ + " \"Effect\": \"Allow\","
+ + " \"Action\": ["
+ + " \"lambda:InvokeFunction\""
+ + " ],"
+ + " \"Resource\": ["
+ + " \"*\""
+ + " ]"
+ + " }"
+ + "]}";
+ var policyRequest =
+ PutRolePolicyRequest.builder()
+ .roleName(sfnName + "-role")
+ .policyName(sfnName + "-policy")
+ .policyDocument(policyDocument)
+ .build();
+ iamClient.putRolePolicy(policyRequest);
+ String stateMachineDefinition =
+ "{"
+ + " \"Comment\": \"A Hello World example of the Amazon States Language using a Pass state\","
+ + " \"StartAt\": \"HelloWorld\","
+ + " \"States\": {"
+ + " \"HelloWorld\": {"
+ + " \"Type\": \"Pass\","
+ + " \"Result\": \"Hello World!\","
+ + " \"End\": true"
+ + " }"
+ + " }"
+ + "}";
+ var sfnRequest =
+ CreateStateMachineRequest.builder()
+ .name(sfnName)
+ .roleArn(roleArn)
+ .definition(stateMachineDefinition)
+ .type(StateMachineType.STANDARD)
+ .build();
+ existingStateMachineArn = sfnClient.createStateMachine(sfnRequest).stateMachineArn();
+ }
+
+ var activityName = "test-activity";
+ String existingActivityArn = null;
+
+ try {
+ var listRequest = ListActivitiesRequest.builder().build();
+ var listResponse = sfnClient.listActivities(listRequest);
+ existingActivityArn =
+ listResponse.activities().stream()
+ .filter(activity -> activity.name().equals(activityName))
+ .findFirst()
+ .map(ActivityListItem::activityArn)
+ .orElse(null);
+ } catch (Exception e) {
+ logger.error("Error listing activities", e);
+ }
+
+ if (existingActivityArn != null) {
+ logger.debug("Activities already exists, skipping creation");
+ } else {
+ logger.debug("Activities not found, creating a new one");
+ var createRequest = CreateActivityRequest.builder().name(activityName).build();
+ existingActivityArn = sfnClient.createActivity(createRequest).activityArn();
+ }
+
+ String finalExistingStateMachineArn = existingStateMachineArn;
+ String finalExistingActivityArn = existingActivityArn;
+
+ get(
+ "/sfn/describestatemachine/:name",
+ (req, res) -> {
+ var describeRequest =
+ DescribeStateMachineRequest.builder()
+ .stateMachineArn(finalExistingStateMachineArn)
+ .build();
+ sfnClient.describeStateMachine(describeRequest);
+ return "";
+ });
+
+ get(
+ "/sfn/describeactivity/:name",
+ (req, res) -> {
+ var describeRequest =
+ DescribeActivityRequest.builder().activityArn(finalExistingActivityArn).build();
+ sfnClient.describeActivity(describeRequest);
+ return "";
+ });
+
+ get(
+ "/sfn/error",
+ (req, res) -> {
+ setMainStatus(400);
+ var errorClient =
+ SfnClient.builder()
+ .endpointOverride(URI.create("http://error.test:8080"))
+ .credentialsProvider(CREDENTIALS_PROVIDER)
+ .build();
+
+ try {
+ var describeRequest =
+ DescribeActivityRequest.builder()
+ .activityArn(
+ "arn:aws:states:us-west-2:000000000000:activity:nonexistent-activity")
+ .build();
+ errorClient.describeActivity(describeRequest);
+ } catch (Exception e) {
+ logger.error("Error describing activity", e);
+ }
+ return "";
+ });
+
+ get(
+ "/sfn/fault",
+ (req, res) -> {
+ setMainStatus(500);
+ var faultClient =
+ SfnClient.builder()
+ .endpointOverride(URI.create("http://fault.test:8080"))
+ .credentialsProvider(CREDENTIALS_PROVIDER)
+ .build();
+
+ try {
+ var describeRequest =
+ DescribeActivityRequest.builder()
+ .activityArn("arn:aws:states:us-west-2:000000000000:activity:fault-activity")
+ .build();
+ faultClient.describeActivity(describeRequest);
+ } catch (Exception e) {
+ logger.error("Error describing activity", e);
+ }
+ return "";
+ });
+ }
+
+ private static void setupSns() {
+ var snsClient =
+ SnsClient.builder()
+ .endpointOverride(endpoint)
+ .credentialsProvider(CREDENTIALS_PROVIDER)
+ .build();
+
+ var topicName = "test-topic";
+ String existingTopicArn = null;
+
+ try {
+ var listRequest = ListTopicsRequest.builder().build();
+ var listResponse = snsClient.listTopics(listRequest);
+ existingTopicArn =
+ listResponse.topics().stream()
+ .filter(topic -> topic.topicArn().contains(topicName))
+ .findFirst()
+ .map(Topic::topicArn)
+ .orElse(null);
+ } catch (Exception e) {
+ logger.error("Error listing topics", e);
+ }
+
+ if (existingTopicArn != null) {
+ logger.debug("Topics already exists, skipping creation");
+ } else {
+ logger.debug("Topics not found, creating a new one");
+ var createTopicRequest = CreateTopicRequest.builder().name(topicName).build();
+ var createTopicResponse = snsClient.createTopic(createTopicRequest);
+ existingTopicArn = createTopicResponse.topicArn();
+ }
+
+ String finalExistingTopicArn = existingTopicArn;
+ get(
+ "/sns/gettopicattributes/:topicId",
+ (req, res) -> {
+ var getTopicAttributesRequest =
+ GetTopicAttributesRequest.builder().topicArn(finalExistingTopicArn).build();
+ snsClient.getTopicAttributes(getTopicAttributesRequest);
+ return "";
+ });
+
+ get(
+ "/sns/error",
+ (req, res) -> {
+ setMainStatus(400);
+ var errorClient =
+ SnsClient.builder()
+ .endpointOverride(URI.create("http://error.test:8080"))
+ .credentialsProvider(CREDENTIALS_PROVIDER)
+ .build();
+
+ try {
+ var getTopicAttributesRequest =
+ GetTopicAttributesRequest.builder()
+ .topicArn("arn:aws:sns:us-west-2:000000000000:nonexistent-topic")
+ .build();
+ errorClient.getTopicAttributes(getTopicAttributesRequest);
+ } catch (Exception e) {
+ logger.error("Error describing topic", e);
+ }
+ return "";
+ });
+
+ get(
+ "/sns/fault",
+ (req, res) -> {
+ setMainStatus(500);
+ var faultClient =
+ SnsClient.builder()
+ .endpointOverride(URI.create("http://fault.test:8080"))
+ .credentialsProvider(CREDENTIALS_PROVIDER)
+ .build();
+
+ try {
+ var getTopicAttributesRequest =
+ GetTopicAttributesRequest.builder()
+ .topicArn("arn:aws:sns:us-west-2:000000000000:fault-topic")
+ .build();
+ faultClient.getTopicAttributes(getTopicAttributesRequest);
+ } catch (Exception e) {
+ logger.error("Error describing topic", e);
+ }
+ return "";
+ });
+ }
+
private static void setupBedrock() {
// Localstack does not support Bedrock related services.
// We point all Bedrock related request endpoints to the local app,
From eae57c577d8fe3c702c0f8d36db9493b19930fe2 Mon Sep 17 00:00:00 2001
From: Prashant Srivastava <50466688+srprash@users.noreply.github.com>
Date: Mon, 2 Dec 2024 20:52:30 -0800
Subject: [PATCH 2/2] APIGW + Lambda sample app (#961)
---
sample-apps/apigateway-lambda/README.md | 58 +++++++++
.../apigateway-lambda/build.gradle.kts | 40 ++++++
.../com/amazon/sampleapp/LambdaHandler.java | 82 ++++++++++++
.../apigateway-lambda/terraform/main.tf | 119 ++++++++++++++++++
.../apigateway-lambda/terraform/variables.tf | 43 +++++++
settings.gradle.kts | 1 +
6 files changed, 343 insertions(+)
create mode 100644 sample-apps/apigateway-lambda/README.md
create mode 100644 sample-apps/apigateway-lambda/build.gradle.kts
create mode 100644 sample-apps/apigateway-lambda/src/main/java/com/amazon/sampleapp/LambdaHandler.java
create mode 100644 sample-apps/apigateway-lambda/terraform/main.tf
create mode 100644 sample-apps/apigateway-lambda/terraform/variables.tf
diff --git a/sample-apps/apigateway-lambda/README.md b/sample-apps/apigateway-lambda/README.md
new file mode 100644
index 0000000000..6ecc80986b
--- /dev/null
+++ b/sample-apps/apigateway-lambda/README.md
@@ -0,0 +1,58 @@
+## API Gateway + Lambda Sample Application
+
+The directory contains the source code and the Infrastructure as Code (IaC) to create the sample app in your AWS account.
+
+### Prerequisite
+Before you begin, ensure you have the following installed:
+- Java 17
+- Gradle
+- Terraform
+- AWS CLI configured with appropriate credentials
+
+### Getting Started
+
+#### 1. Build the application
+```bash
+# Change to the project directory
+cd sample-apps/apigateway-lambda
+
+# Build the application using Gradle
+gradle clean build
+
+# Prepare the Lambda deployment package
+gradle createLambdaZip
+```
+
+#### 2. Deploy the application
+```bash
+# Change to the terraform directory
+cd terraform
+
+# Initialize Terraform
+terraform init
+
+# (Optional) Review the deployment plan for better understanding of the components
+terraform plan
+
+# Deploy
+terraform apply
+```
+
+#### 3. Testing the applicating
+After successful deployment, Terraform will output the API Gateway endpoint URL. You can test the application using:
+```bash
+curl
+```
+
+#### 4. Clean Up
+To avoid incurring unnecessary charges, remember to destroy the resources when you are done:
+```bash
+terraform destroy
+```
+
+#### (Optional) Instrumenting with Application Signals Lambda Layer
+You can choose to instrument the Lambda function with Application Signals Lambda Layer upon deployment by passing in the layer ARN to the `adot_layer_arn` variable.
+You must have the layer already published to your account before executing the following command.
+```bash
+terraform apply -var "adot_layer_arn="
+```
\ No newline at end of file
diff --git a/sample-apps/apigateway-lambda/build.gradle.kts b/sample-apps/apigateway-lambda/build.gradle.kts
new file mode 100644
index 0000000000..66992540ab
--- /dev/null
+++ b/sample-apps/apigateway-lambda/build.gradle.kts
@@ -0,0 +1,40 @@
+plugins {
+ java
+ application
+}
+
+application {
+ mainClass.set("com.amazon.sampleapp.LambdaHandler")
+}
+
+java {
+ toolchain {
+ languageVersion.set(JavaLanguageVersion.of(17))
+ }
+}
+
+dependencies {
+ implementation("com.amazonaws:aws-lambda-java-core:1.2.2")
+ implementation("com.squareup.okhttp3:okhttp:4.11.0")
+ implementation("software.amazon.awssdk:s3:2.29.23")
+ implementation("org.json:json:20240303")
+ implementation("org.slf4j:jcl-over-slf4j:2.0.16")
+ testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
+}
+
+tasks.jar {
+ manifest {
+ attributes["Main-Class"] = "com.amazon.sampleapp.LambdaHandler"
+ }
+}
+
+tasks.register("createLambdaZip") {
+ dependsOn("build")
+ from(tasks.compileJava.get())
+ from(tasks.processResources.get())
+ into("lib") {
+ from(configurations.runtimeClasspath.get())
+ }
+ archiveFileName.set("lambda-function.zip")
+ destinationDirectory.set(layout.buildDirectory.dir("distributions"))
+}
diff --git a/sample-apps/apigateway-lambda/src/main/java/com/amazon/sampleapp/LambdaHandler.java b/sample-apps/apigateway-lambda/src/main/java/com/amazon/sampleapp/LambdaHandler.java
new file mode 100644
index 0000000000..f3e11bc38d
--- /dev/null
+++ b/sample-apps/apigateway-lambda/src/main/java/com/amazon/sampleapp/LambdaHandler.java
@@ -0,0 +1,82 @@
+package com.amazon.sampleapp;
+
+import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.RequestHandler;
+import java.io.IOException;
+import java.util.Map;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.json.JSONObject;
+import software.amazon.awssdk.services.s3.S3Client;
+import software.amazon.awssdk.services.s3.model.HeadBucketRequest;
+import software.amazon.awssdk.services.s3.model.S3Exception;
+
+public class LambdaHandler implements RequestHandler