diff --git a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/AwsSdk/ArnBuilder.cs b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/AwsSdk/ArnBuilder.cs
index b067b5b06b..86ba2deab3 100644
--- a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/AwsSdk/ArnBuilder.cs
+++ b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/AwsSdk/ArnBuilder.cs
@@ -138,7 +138,7 @@ public override string ToString()
string partition = string.IsNullOrEmpty(Partition) ? "[Missing]" : Partition;
string region = string.IsNullOrEmpty(Region) ? "[Missing]" : Region;
string idPresent = string.IsNullOrEmpty(AccountId) ? "[Missing]" : "[Present]";
-
+
return $"Partition: {partition}, Region: {region}, AccountId: {idPresent}";
}
@@ -155,6 +155,5 @@ private string ConstructArn(string partition, string service, string region, str
}
return "arn:" + partition + ":" + service + ":" + region + ":" + accountId + ":" + resource;
}
-
}
}
diff --git a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/AwsSdk/AwsAccountIdDecoder.cs b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/AwsSdk/AwsAccountIdDecoder.cs
index a00be1f478..a6e3dea2fb 100644
--- a/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/AwsSdk/AwsAccountIdDecoder.cs
+++ b/src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/AwsSdk/AwsAccountIdDecoder.cs
@@ -31,13 +31,20 @@ public static string GetAccountId(string awsAccessKeyId)
///
/// Performs a Base-32 decode of the specified input string.
/// Allowed character range is a-z and 2-7. 'a' being 0 and '7' is 31.
+ ///
+ /// public to allow for unit testing
///
/// The string to be decoded. Must be at least 10 characters.
/// A long containing first 6 bytes of the base 32 decoded data.
/// If src has less than 10 characters.
/// If src contains invalid characters for Base-32
- private static long Base32Decode(string src)
+ public static long Base32Decode(string src)
{
+ if (string.IsNullOrEmpty(src))
+ {
+ throw new ArgumentNullException(nameof(src), "The input string cannot be null or empty.");
+ }
+
if (src.Length < 10)
{
throw new ArgumentException("The input string must be at least 10 characters long.", nameof(src));
diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/AmazonServiceClientWrapper.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/AmazonServiceClientWrapper.cs
index 615a9ea8e5..62e2c72aec 100644
--- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/AmazonServiceClientWrapper.cs
+++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/AmazonServiceClientWrapper.cs
@@ -39,6 +39,9 @@ public AfterWrappedMethodDelegate BeforeWrappedMethod(InstrumentedMethodCall ins
// convert the access key to an account id
AwsAccountId = AwsAccountIdDecoder.GetAccountId(accessKey);
+
+ // TODO: TESTING ONLY
+ agent.Logger.Info($"Successfully parsed AWS AccountId: {AwsAccountId}");
}
catch (Exception e)
{
diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/AwsSdkPipelineWrapper.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/AwsSdkPipelineWrapper.cs
index e473c89968..a77591ed98 100644
--- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/AwsSdkPipelineWrapper.cs
+++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/AwsSdkPipelineWrapper.cs
@@ -33,10 +33,13 @@ private ArnBuilder CreateArnBuilder(IAgent agent, dynamic requestContext)
try
{
accountId = GetAccountId(agent);
- var clientconfig = requestContext.ClientConfig;
- var regionEndpoint = clientconfig.RegionEndpoint;
- systemName = regionEndpoint.SystemName;
- partition = regionEndpoint.PartitionName;
+ var clientConfig = requestContext.ClientConfig;
+ if (clientConfig.RegionEndpoint != null)
+ {
+ var regionEndpoint = clientConfig.RegionEndpoint;
+ systemName = regionEndpoint.SystemName;
+ partition = regionEndpoint.PartitionName;
+ }
}
catch (Exception e)
{
@@ -46,8 +49,10 @@ private ArnBuilder CreateArnBuilder(IAgent agent, dynamic requestContext)
_reportBadArnBuilder = false;
}
}
-
- return new ArnBuilder(partition, systemName, accountId);
+ agent.Logger.Debug($"AwsSdkPipelineWrapper: Creating ArnBuilder with partition: {partition}, systemName: {systemName}, accountId: {accountId}");
+ var arnBuilder = new ArnBuilder(partition, systemName, accountId);
+ agent.Logger.Debug($"AwsSdkPipelineWrapper: ArnBuilder created: {arnBuilder}");
+ return arnBuilder;
}
private string GetAccountId(IAgent agent)
@@ -66,6 +71,8 @@ private string GetAccountId(IAgent agent)
}
}
+ // TODO: testing only
+ agent.Logger.Debug($"AwsSdkPipelineWrapper: Using accountId: {accountId}");
return accountId;
}
@@ -104,17 +111,19 @@ public AfterWrappedMethodDelegate BeforeWrappedMethod(InstrumentedMethodCall ins
{
return SQSRequestHandler.HandleSQSRequest(instrumentedMethodCall, agent, transaction, request, isAsync, executionContext);
}
- else if (requestType == "Amazon.Lambda.Model.InvokeRequest")
+
+ if (requestType == "Amazon.Lambda.Model.InvokeRequest")
{
return LambdaInvokeRequestHandler.HandleInvokeRequest(instrumentedMethodCall, agent, transaction, request, isAsync, builder);
- }
- else if (requestType.StartsWith("Amazon.DynamoDBv2"))
+ }
+
+ if (requestType.StartsWith("Amazon.DynamoDBv2"))
{
- return DynamoDbRequestHandler.HandleDynamoDbRequest(instrumentedMethodCall, agent, transaction, request, isAsync, executionContext, builder);
+ return DynamoDbRequestHandler.HandleDynamoDbRequest(instrumentedMethodCall, agent, transaction, request, isAsync, builder);
}
if (!_unsupportedRequestTypes.Contains(requestType)) // log once per unsupported request type
- {
+ {
agent.Logger.Debug($"AwsSdkPipelineWrapper: Unsupported request type: {requestType}. Returning NoOp delegate.");
_unsupportedRequestTypes.Add(requestType);
}
diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/RequestHandlers/DynamoDbRequestHandler.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/RequestHandlers/DynamoDbRequestHandler.cs
index a559fcc94e..4d3d2d220a 100644
--- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/RequestHandlers/DynamoDbRequestHandler.cs
+++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/AwsSdk/RequestHandlers/DynamoDbRequestHandler.cs
@@ -15,7 +15,7 @@ internal static class DynamoDbRequestHandler
private static readonly ConcurrentDictionary _operationNameCache = new();
- public static AfterWrappedMethodDelegate HandleDynamoDbRequest(InstrumentedMethodCall instrumentedMethodCall, IAgent agent, ITransaction transaction, dynamic request, bool isAsync, dynamic executionContext, ArnBuilder builder)
+ public static AfterWrappedMethodDelegate HandleDynamoDbRequest(InstrumentedMethodCall instrumentedMethodCall, IAgent agent, ITransaction transaction, dynamic request, bool isAsync, ArnBuilder builder)
{
var requestType = ((object)request).GetType().Name;
@@ -29,8 +29,12 @@ public static AfterWrappedMethodDelegate HandleDynamoDbRequest(InstrumentedMetho
// TODO: The entity relationship docs suggest cloud.resource_id should be a span attribute, so maybe we added it to the DataStore segment below instead??
var arn = builder.Build("dynamodb", $"table/{model}");
- if (string.IsNullOrEmpty(arn))
+ if (!string.IsNullOrEmpty(arn))
transaction.AddCloudSdkAttribute("cloud.resource_id", arn);
+ else
+ {
+ agent.Logger.Debug($"Unable to build ARN for DynamoDB request. ArnBuilder reports: {builder}");
+ }
var segment = transaction.StartDatastoreSegment(instrumentedMethodCall.MethodCall, new ParsedSqlStatement(DatastoreVendor.DynamoDB, model, operation), isLeaf: true);
diff --git a/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkExercisers/AwsSdkDynamoDBExerciser.cs b/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkExercisers/AwsSdkDynamoDBExerciser.cs
index e07fe0aa84..859b74c3aa 100644
--- a/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkExercisers/AwsSdkDynamoDBExerciser.cs
+++ b/tests/Agent/IntegrationTests/ContainerApplications/AwsSdkTestApp/AwsSdkExercisers/AwsSdkDynamoDBExerciser.cs
@@ -8,7 +8,7 @@
using System;
using System.Collections.Generic;
using Amazon.Runtime;
-using System.Threading;
+using Amazon;
namespace AwsSdkTestApp.AwsSdkExercisers
{
@@ -25,12 +25,16 @@ public AwsSdkDynamoDBExerciser()
private AmazonDynamoDBClient GetDynamoDBClient()
{
- AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig();
- // Set the endpoint URL
- clientConfig.ServiceURL = "http://dynamodb:8000"; // port must match what is set in docker compose
- clientConfig.AuthenticationRegion = "us-west-2";
- var creds = new BasicAWSCredentials("xxx", "xxx");
- AmazonDynamoDBClient client = new AmazonDynamoDBClient(creds, clientConfig);
+ AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig
+ {
+ // Set the endpoint URL
+ ServiceURL = "http://dynamodb:8000", // port must match what is set in docker compose
+ AuthenticationRegion = "us-west-2",
+ RegionEndpoint = RegionEndpoint.USWest2
+ };
+
+ // use plausible (but fake) access key and fake secret key so account id parsing can be tested
+ AmazonDynamoDBClient client = new AmazonDynamoDBClient("FOOIHSFODNNAEXAMPLE", "MOREGIBBERISH", clientConfig);
return client;
}
diff --git a/tests/Agent/IntegrationTests/ContainerIntegrationTests/Fixtures/AwsSdkContainerTestFixtures.cs b/tests/Agent/IntegrationTests/ContainerIntegrationTests/Fixtures/AwsSdkContainerTestFixtures.cs
index b70159f279..d8dad5675d 100644
--- a/tests/Agent/IntegrationTests/ContainerIntegrationTests/Fixtures/AwsSdkContainerTestFixtures.cs
+++ b/tests/Agent/IntegrationTests/ContainerIntegrationTests/Fixtures/AwsSdkContainerTestFixtures.cs
@@ -32,7 +32,7 @@ public class AwsSdkContainerSQSTestFixture : AwsSdkContainerTestFixtureBase
{
private const string Dockerfile = "AwsSdkTestApp/Dockerfile";
private const ContainerApplication.Architecture Architecture = ContainerApplication.Architecture.X64;
- private const string DistroTag = "jammy";
+ private const string DistroTag = "noble";
private readonly string BaseUrl;
@@ -78,7 +78,7 @@ public class AwsSdkContainerDynamoDBTestFixture : AwsSdkContainerTestFixtureBase
{
private const string Dockerfile = "AwsSdkTestApp/Dockerfile";
private const ContainerApplication.Architecture Architecture = ContainerApplication.Architecture.X64;
- private const string DistroTag = "jammy";
+ private const string DistroTag = "noble";
private readonly string BaseUrl;
diff --git a/tests/Agent/IntegrationTests/ContainerIntegrationTests/Tests/AwsSdk/AwsSdkDynamoDBTest.cs b/tests/Agent/IntegrationTests/ContainerIntegrationTests/Tests/AwsSdk/AwsSdkDynamoDBTest.cs
index c136e83bf7..ed9e7955be 100644
--- a/tests/Agent/IntegrationTests/ContainerIntegrationTests/Tests/AwsSdk/AwsSdkDynamoDBTest.cs
+++ b/tests/Agent/IntegrationTests/ContainerIntegrationTests/Tests/AwsSdk/AwsSdkDynamoDBTest.cs
@@ -23,6 +23,8 @@ protected AwsSdkDynamoDBTestBase(AwsSdkContainerDynamoDBTestFixture fixture, ITe
_fixture = fixture;
_fixture.TestLogger = output;
+ _fixture.SetAdditionalEnvironmentVariable("AWSSDK_INITCOLLECTIONS", "true");
+
_fixture.Actions(setupConfiguration: () =>
{
var configModifier = new NewRelicConfigModifier(_fixture.DestinationNewRelicConfigFilePath);
@@ -54,9 +56,9 @@ protected AwsSdkDynamoDBTestBase(AwsSdkContainerDynamoDBTestFixture fixture, ITe
_fixture.AgentLog.WaitForLogLine(AgentLogBase.MetricDataLogLineRegex, TimeSpan.FromMinutes(2));
_fixture.AgentLog.WaitForLogLine(AgentLogBase.TransactionTransformCompletedLogLineRegex, TimeSpan.FromMinutes(2));
- // shut down the container and wait for the agent log to see it
- _fixture.ShutdownRemoteApplication();
- _fixture.AgentLog.WaitForLogLine(AgentLogBase.ShutdownLogLineRegex, TimeSpan.FromSeconds(10));
+ //// shut down the container and wait for the agent log to see it
+ //_fixture.ShutdownRemoteApplication();
+ //_fixture.AgentLog.WaitForLogLine(AgentLogBase.ShutdownLogLineRegex, TimeSpan.FromSeconds(10));
});
_fixture.Initialize();
diff --git a/tests/Agent/IntegrationTests/ContainerIntegrationTests/Tests/AwsSdk/AwsSdkSQSTest.cs b/tests/Agent/IntegrationTests/ContainerIntegrationTests/Tests/AwsSdk/AwsSdkSQSTest.cs
index edd06af3fb..9015497449 100644
--- a/tests/Agent/IntegrationTests/ContainerIntegrationTests/Tests/AwsSdk/AwsSdkSQSTest.cs
+++ b/tests/Agent/IntegrationTests/ContainerIntegrationTests/Tests/AwsSdk/AwsSdkSQSTest.cs
@@ -40,7 +40,7 @@ protected AwsSdkSQSTestBase(AwsSdkContainerSQSTestFixture fixture, ITestOutputHe
configModifier.ConfigureFasterMetricsHarvestCycle(15);
configModifier.ConfigureFasterSpanEventsHarvestCycle(15);
configModifier.ConfigureFasterTransactionTracesHarvestCycle(15);
- configModifier.LogToConsole();
+ //configModifier.LogToConsole();
},
exerciseApplication: () =>
diff --git a/tests/Agent/IntegrationTests/IntegrationTestHelpers/RemoteServiceFixtures/RemoteApplicationFixture.cs b/tests/Agent/IntegrationTests/IntegrationTestHelpers/RemoteServiceFixtures/RemoteApplicationFixture.cs
index c665fe3052..bfb7db67fe 100644
--- a/tests/Agent/IntegrationTests/IntegrationTestHelpers/RemoteServiceFixtures/RemoteApplicationFixture.cs
+++ b/tests/Agent/IntegrationTests/IntegrationTestHelpers/RemoteServiceFixtures/RemoteApplicationFixture.cs
@@ -351,6 +351,7 @@ public virtual void Initialize()
catch (Exception ex)
{
TestLogger?.WriteLine("Exception occurred in Initialize: " + ex.ToString());
+ AgentLogExpected = false;
throw;
}
finally
diff --git a/tests/Agent/IntegrationTests/Shared/AwsBedrockConfiguration.cs b/tests/Agent/IntegrationTests/Shared/AwsConfiguration.cs
similarity index 96%
rename from tests/Agent/IntegrationTests/Shared/AwsBedrockConfiguration.cs
rename to tests/Agent/IntegrationTests/Shared/AwsConfiguration.cs
index fc446cb945..26158c52b0 100644
--- a/tests/Agent/IntegrationTests/Shared/AwsBedrockConfiguration.cs
+++ b/tests/Agent/IntegrationTests/Shared/AwsConfiguration.cs
@@ -3,7 +3,7 @@
namespace NewRelic.Agent.IntegrationTests.Shared
{
- public class AwsBedrockConfiguration
+ public class AwsConfiguration
{
public static string AwsAccessKeyId
{
diff --git a/tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/NetStandardLibraries/LLM/BedrockModels.cs b/tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/NetStandardLibraries/LLM/BedrockModels.cs
index f7d16f8a9e..d2731d06d5 100644
--- a/tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/NetStandardLibraries/LLM/BedrockModels.cs
+++ b/tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/NetStandardLibraries/LLM/BedrockModels.cs
@@ -20,7 +20,7 @@ namespace MultiFunctionApplicationHelpers.NetStandardLibraries.LLM
internal class BedrockModels
{
private static readonly AmazonBedrockRuntimeClient _amazonBedrockRuntimeClient =
- new AmazonBedrockRuntimeClient(AwsBedrockConfiguration.AwsAccessKeyId, AwsBedrockConfiguration.AwsSecretAccessKey, AwsBedrockConfiguration.AwsRegion.ToRegionId());
+ new AmazonBedrockRuntimeClient(AwsConfiguration.AwsAccessKeyId, AwsConfiguration.AwsSecretAccessKey, AwsConfiguration.AwsRegion.ToRegionId());
[MethodImpl(MethodImplOptions.NoInlining)]
public static async Task InvokeAmazonEmbedAsync(string prompt, bool generateError) => await InvokeTitanAsync(prompt, true, generateError);
diff --git a/tests/Agent/UnitTests/NewRelic.Agent.Extensions.Tests/Helpers/AwsAccountIdDecoderTests.cs b/tests/Agent/UnitTests/NewRelic.Agent.Extensions.Tests/Helpers/AwsAccountIdDecoderTests.cs
new file mode 100644
index 0000000000..c884ed0f16
--- /dev/null
+++ b/tests/Agent/UnitTests/NewRelic.Agent.Extensions.Tests/Helpers/AwsAccountIdDecoderTests.cs
@@ -0,0 +1,102 @@
+// Copyright 2020 New Relic, Inc. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+using System;
+using NewRelic.Agent.Extensions.AwsSdk;
+using NUnit.Framework;
+using Telerik.JustMock;
+
+namespace Agent.Extensions.Tests.Helpers
+{
+ [TestFixture]
+ internal class AwsAccountIdDecoderTests
+ {
+ [Test]
+ public void GetAccountId_ValidAwsAccessKeyId_ReturnsExpectedAccountId()
+ {
+ // Arrange
+ string awsAccessKeyId = "AKIAIOSFODNN7EXAMPLE"; // not a real AWS access key!
+ string expectedAccountId = "581039954779";
+
+ // Act
+ string actualAccountId = AwsAccountIdDecoder.GetAccountId(awsAccessKeyId);
+
+ // Assert
+ Assert.That(expectedAccountId, Is.EqualTo(actualAccountId));
+ }
+
+ [Test]
+ public void GetAccountId_NullOrEmptyAwsAccessKeyId_ThrowsArgumentNullException()
+ {
+ // Arrange
+ string awsAccessKeyId = null;
+
+ // Act & Assert
+ var ex = Assert.Throws(() => AwsAccountIdDecoder.GetAccountId(awsAccessKeyId));
+ Assert.That(ex.ParamName, Is.EqualTo("awsAccessKeyId"));
+ }
+
+ [Test]
+ public void GetAccountId_ShortAwsAccessKeyId_ThrowsArgumentOutOfRangeException()
+ {
+ // Arrange
+ string awsAccessKeyId = "AKIAIOSFODN";
+
+ // Act & Assert
+ var ex = Assert.Throws(() => AwsAccountIdDecoder.GetAccountId(awsAccessKeyId));
+ Assert.That(ex.ParamName, Is.EqualTo("awsAccessKeyId"));
+ }
+
+ [Test]
+ public void Base32Decode_ShortString_ThrowsArgumentException()
+ {
+ // Arrange
+ string shortString = "shortstr";
+
+ // Act & Assert
+ var ex = Assert.Throws(() => AwsAccountIdDecoder.Base32Decode(shortString));
+ Assert.That(ex.ParamName, Is.EqualTo("src"));
+ }
+
+ [Test]
+ public void Base32Decode_InvalidCharacters_ThrowsArgumentOutOfRangeException()
+ {
+ // Arrange
+ string invalidBase32String = "someBogusbase32string";
+
+ // Act & Assert
+ var ex = Assert.Throws(() => AwsAccountIdDecoder.Base32Decode(invalidBase32String));
+ Assert.That(ex.ParamName, Is.EqualTo("src"));
+ }
+
+ [Test]
+ public void Base32Decode_NullOrEmptyString_ThrowsArgumentNullException()
+ {
+ // Arrange
+ string nullString = null;
+ string emptyString = string.Empty;
+
+ // Act & Assert
+ var exNull = Assert.Throws(() => AwsAccountIdDecoder.Base32Decode(nullString));
+ Assert.That(exNull.ParamName, Is.EqualTo("src"));
+
+ var exEmpty = Assert.Throws(() => AwsAccountIdDecoder.Base32Decode(emptyString));
+ Assert.That(exEmpty.ParamName, Is.EqualTo("src"));
+ }
+
+ [Test]
+ public void Base32Decode_ValidBase32String_ReturnsDecodedLong()
+ {
+ // Arrange
+ string validBase32String = "iosfodnn7example"; // Example valid Base32 string (10 characters)
+ long expectedDecodedValue = 74373114211833L;
+
+ // Act
+ long decodedValue = AwsAccountIdDecoder.Base32Decode(validBase32String);
+
+ // Assert
+ Assert.That(decodedValue, Is.EqualTo(expectedDecodedValue)); // Adjust expected value based on actual decoding logic
+ }
+
+ }
+}