Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/next-release/feature-AWSSDKforJavav2-7c6c7a4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "feature",
"category": "AWS SDK for Java v2",
"contributor": "",
"description": "Add business metric support for Sigv4A auth scheme to track when an operation is called using Sigv4A signing"
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,20 @@ private MethodSpec generateBeforeExecution() {
builder.addStatement("recordEnvironmentTokenBusinessMetric(selectedAuthScheme, "
+ "executionAttributes)");
}

if (authSchemeSpecUtils.hasSigV4aSupport()) {
builder.beginControlFlow("if (selectedAuthScheme != null && "
+ "selectedAuthScheme.authSchemeOption().schemeId().equals($S))",
"aws.auth#sigv4a")
.addStatement("$T businessMetrics = executionAttributes.getAttribute($T.BUSINESS_METRICS)",
ClassName.get("software.amazon.awssdk.core.useragent", "BusinessMetricCollection"),
SdkInternalExecutionAttribute.class)
.beginControlFlow("if (businessMetrics != null)")
.addStatement("businessMetrics.addMetric($T.SIGV4A_SIGNING.value())",
BusinessMetricFeatureId.class)
.endControlFlow()
.endControlFlow();
}
return builder.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute;
import software.amazon.awssdk.core.internal.util.MetricUtils;
import software.amazon.awssdk.core.metrics.CoreMetric;
import software.amazon.awssdk.core.useragent.BusinessMetricCollection;
import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.http.auth.aws.signer.RegionSet;
import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme;
import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption;
Expand Down Expand Up @@ -49,6 +51,13 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes
List<AuthSchemeOption> authOptions = resolveAuthOptions(context, executionAttributes);
SelectedAuthScheme<? extends Identity> selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes);
putSelectedAuthScheme(executionAttributes, selectedAuthScheme);
if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a")) {
BusinessMetricCollection businessMetrics = executionAttributes
.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS);
if (businessMetrics != null) {
businessMetrics.addMetric(BusinessMetricFeatureId.SIGV4A_SIGNING.value());
}
}
}

private List<AuthSchemeOption> resolveAuthOptions(Context.BeforeExecution context, ExecutionAttributes executionAttributes) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute;
import software.amazon.awssdk.core.internal.util.MetricUtils;
import software.amazon.awssdk.core.metrics.CoreMetric;
import software.amazon.awssdk.core.useragent.BusinessMetricCollection;
import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.endpoints.EndpointProvider;
import software.amazon.awssdk.http.auth.aws.signer.RegionSet;
import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme;
Expand Down Expand Up @@ -52,6 +54,13 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes
List<AuthSchemeOption> authOptions = resolveAuthOptions(context, executionAttributes);
SelectedAuthScheme<? extends Identity> selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes);
putSelectedAuthScheme(executionAttributes, selectedAuthScheme);
if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a")) {
BusinessMetricCollection businessMetrics = executionAttributes
.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS);
if (businessMetrics != null) {
businessMetrics.addMetric(BusinessMetricFeatureId.SIGV4A_SIGNING.value());
}
}
}

private List<AuthSchemeOption> resolveAuthOptions(Context.BeforeExecution context, ExecutionAttributes executionAttributes) {
Expand Down Expand Up @@ -102,7 +111,6 @@ private QueryAuthSchemeParams authSchemeParams(SdkRequest request, ExecutionAttr
executionAttributes.getOptionalAttribute(AwsExecutionAttribute.AWS_SIGV4A_SIGNING_REGION_SET)
.filter(regionSet -> !CollectionUtils.isNullOrEmpty(regionSet))
.ifPresent(nonEmptyRegionSet -> builder.regionSet(RegionSet.create(nonEmptyRegionSet)));

if (builder instanceof QueryEndpointResolverAware.Builder) {
EndpointProvider endpointProvider = executionAttributes.getAttribute(SdkInternalExecutionAttribute.ENDPOINT_PROVIDER);
if (endpointProvider instanceof QueryEndpointProvider) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute;
import software.amazon.awssdk.core.internal.util.MetricUtils;
import software.amazon.awssdk.core.metrics.CoreMetric;
import software.amazon.awssdk.core.useragent.BusinessMetricCollection;
import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.endpoints.EndpointProvider;
import software.amazon.awssdk.http.auth.aws.signer.RegionSet;
import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme;
Expand Down Expand Up @@ -52,6 +54,13 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes
List<AuthSchemeOption> authOptions = resolveAuthOptions(context, executionAttributes);
SelectedAuthScheme<? extends Identity> selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes);
putSelectedAuthScheme(executionAttributes, selectedAuthScheme);
if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a")) {
BusinessMetricCollection businessMetrics = executionAttributes
.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS);
if (businessMetrics != null) {
businessMetrics.addMetric(BusinessMetricFeatureId.SIGV4A_SIGNING.value());
}
}
}

private List<AuthSchemeOption> resolveAuthOptions(Context.BeforeExecution context, ExecutionAttributes executionAttributes) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
/**
* An enum class representing a short form of identity providers to record in the UA string.
*
* Unimplemented metrics: I,J,K,O,S,U-c
* Unimplemented metrics: I,J,K,O,U-c
* Unsupported metrics (these will never be added): A,H
*/
@SdkProtectedApi
Expand All @@ -40,6 +40,7 @@ public enum BusinessMetricFeatureId {
ACCOUNT_ID_MODE_PREFERRED("P"),
ACCOUNT_ID_MODE_DISABLED("Q"),
ACCOUNT_ID_MODE_REQUIRED("R"),
SIGV4A_SIGNING("S"),
RESOLVED_ACCOUNT_ID("T"),
DDB_MAPPER("d"),
BEARER_SERVICE_ENV_VARS("3"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.services;

import static org.assertj.core.api.Assertions.assertThat;
import static software.amazon.awssdk.core.useragent.BusinessMetricCollection.METRIC_SEARCH_PATTERN;

import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.http.AbortableInputStream;
import software.amazon.awssdk.http.HttpExecuteResponse;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.SdkHttpResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonAsyncClient;
import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonClient;
import software.amazon.awssdk.services.sigv4aauth.Sigv4AauthAsyncClient;
import software.amazon.awssdk.services.sigv4aauth.Sigv4AauthClient;
import software.amazon.awssdk.testutils.service.http.MockAsyncHttpClient;
import software.amazon.awssdk.testutils.service.http.MockSyncHttpClient;
import software.amazon.awssdk.utils.StringInputStream;

/**
* Test class to verify that SIGV4A_SIGNING business metric is correctly included
* in the User-Agent header when operation called using Sigv4A signing.
*/
class Sigv4aBusinessMetricUserAgentTest {
private static final String USER_AGENT_HEADER_NAME = "User-Agent";
private static final StaticCredentialsProvider CREDENTIALS_PROVIDER =
StaticCredentialsProvider.create(AwsBasicCredentials.create("akid", "skid"));

private MockSyncHttpClient mockHttpClient;
private MockAsyncHttpClient mockAsyncHttpClient;

@BeforeEach
public void setup() {
mockHttpClient = new MockSyncHttpClient();
mockHttpClient.stubNextResponse(mockResponse());

mockAsyncHttpClient = new MockAsyncHttpClient();
mockAsyncHttpClient.stubNextResponse(mockResponse());
}

@Test
void when_sigv4aServiceIsUsed_correctMetricIsAdded() {
Sigv4AauthClient client = Sigv4AauthClient.builder()
.region(Region.US_WEST_2)
.credentialsProvider(CREDENTIALS_PROVIDER)
.httpClient(mockHttpClient)
.build();

client.simpleOperationWithNoEndpointParams(r -> r.stringMember("test"));

String userAgent = getUserAgentFromLastRequest();
System.out.println("SigV4a service User-Agent: " + userAgent);
assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value()));
}

@Test
void when_sigv4aServiceIsUsedAsync_correctMetricIsAdded() {
Sigv4AauthAsyncClient asyncClient = Sigv4AauthAsyncClient.builder()
.region(Region.US_WEST_2)
.credentialsProvider(CREDENTIALS_PROVIDER)
.httpClient(mockAsyncHttpClient)
.build();

asyncClient.simpleOperationWithNoEndpointParams(r -> r.stringMember("test")).join();

String userAgent = getUserAgentFromLastAsyncRequest();
System.out.println("SigV4a async service User-Agent: " + userAgent);
assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value()));
}

@Test
void when_regularServiceIsUsed_sigv4aMetricIsNotAdded() {
ProtocolRestJsonClient client = ProtocolRestJsonClient.builder()
.region(Region.US_WEST_2)
.credentialsProvider(CREDENTIALS_PROVIDER)
.httpClient(mockHttpClient)
.build();

client.allTypes(r -> {});

String userAgent = getUserAgentFromLastRequest();
System.out.println("Regular service User-Agent: " + userAgent);
assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value()));
}

@Test
void when_regularServiceIsUsedAsync_sigv4aMetricIsNotAdded() {
ProtocolRestJsonAsyncClient asyncClient = ProtocolRestJsonAsyncClient.builder()
.region(Region.US_WEST_2)
.credentialsProvider(CREDENTIALS_PROVIDER)
.httpClient(mockAsyncHttpClient)
.build();

asyncClient.allTypes(r -> {}).join();

String userAgent = getUserAgentFromLastAsyncRequest();
System.out.println("Regular async service User-Agent: " + userAgent);
assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value()));
}

private String getUserAgentFromLastRequest() {
SdkHttpRequest lastRequest = mockHttpClient.getLastRequest();
assertThat(lastRequest).isNotNull();

List<String> userAgentHeaders = lastRequest.headers().get(USER_AGENT_HEADER_NAME);
assertThat(userAgentHeaders).isNotNull().hasSize(1);
return userAgentHeaders.get(0);
}

private String getUserAgentFromLastAsyncRequest() {
SdkHttpRequest lastRequest = mockAsyncHttpClient.getLastRequest();
assertThat(lastRequest).isNotNull();

List<String> userAgentHeaders = lastRequest.headers().get(USER_AGENT_HEADER_NAME);
assertThat(userAgentHeaders).isNotNull().hasSize(1);
return userAgentHeaders.get(0);
}

private static HttpExecuteResponse mockResponse() {
return HttpExecuteResponse.builder()
.response(SdkHttpResponse.builder().statusCode(200).build())
.responseBody(AbortableInputStream.create(new StringInputStream("{}")))
.build();
}
}
Loading