From faee8bbf9a56ce379ed9bb24708bc0d80f4249bf Mon Sep 17 00:00:00 2001 From: 0marperez Date: Mon, 14 Oct 2024 18:53:23 -0400 Subject: [PATCH 01/20] misc: emit credentials business metrics --- .../DefaultChainCredentialsProvider.kt | 14 ++- .../credentials/EcsCredentialsProvider.kt | 5 + .../EnvironmentCredentialsProvider.kt | 5 + .../credentials/ImdsCredentialsProvider.kt | 4 + .../credentials/ProcessCredentialsProvider.kt | 4 + .../credentials/ProfileCredentialsProvider.kt | 94 ++++++++++++------- .../credentials/SsoCredentialsProvider.kt | 4 + .../StsAssumeRoleCredentialsProvider.kt | 4 + .../StsWebIdentityCredentialsProvider.kt | 5 + .../SystemPropertyCredentialsProvider.kt | 5 + aws-runtime/aws-http/api/aws-http.api | 25 +++++ .../BusinessMetricsInterceptor.kt | 22 +++++ .../codegen/BusinessMetricsIntegration.kt | 21 ++++- 13 files changed, 172 insertions(+), 40 deletions(-) diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt index 08b16646a05..79f00909b82 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt @@ -7,12 +7,17 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.config.imds.ImdsClient +import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.* +import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.Attributes +import aws.smithy.kotlin.runtime.collections.MutableAttributes +import aws.smithy.kotlin.runtime.collections.merge import aws.smithy.kotlin.runtime.http.engine.DefaultHttpEngine import aws.smithy.kotlin.runtime.http.engine.HttpClientEngine import aws.smithy.kotlin.runtime.io.Closeable import aws.smithy.kotlin.runtime.io.closeIfCloseable +import aws.smithy.kotlin.runtime.operation.ExecutionContext import aws.smithy.kotlin.runtime.util.PlatformProvider /** @@ -45,7 +50,7 @@ public class DefaultChainCredentialsProvider constructor( httpClient: HttpClientEngine? = null, public val region: String? = null, ) : CloseableCredentialsProvider { - + private val executionContext = ExecutionContext() private val manageEngine = httpClient == null private val engine = httpClient ?: DefaultHttpEngine() @@ -53,7 +58,7 @@ public class DefaultChainCredentialsProvider constructor( SystemPropertyCredentialsProvider(platformProvider::getProperty), EnvironmentCredentialsProvider(platformProvider::getenv), LazilyInitializedCredentialsProvider("EnvironmentStsWebIdentityCredentialsProvider") { - // STS web identity provider can be constructed from either the profile OR 100% from the environment + executionContext.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN) StsWebIdentityCredentialsProvider.fromEnvironment( platformProvider = platformProvider, httpClient = httpClient, @@ -75,7 +80,10 @@ public class DefaultChainCredentialsProvider constructor( private val provider = CachedCredentialsProvider(chain) - override suspend fun resolve(attributes: Attributes): Credentials = provider.resolve(attributes) + override suspend fun resolve(attributes: Attributes): Credentials { + if (attributes is MutableAttributes) (this as MutableAttributes).merge(executionContext) + return provider.resolve(attributes) + } override fun close() { provider.close() diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt index ce0a4e76ef3..6250a9db725 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt @@ -7,8 +7,10 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting +import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric import aws.smithy.kotlin.runtime.ErrorMetadata import aws.smithy.kotlin.runtime.auth.awscredentials.* +import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.client.endpoints.Endpoint import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.config.resolve @@ -76,6 +78,9 @@ public class EcsCredentialsProvider( override suspend fun resolve(attributes: Attributes): Credentials { val logger = coroutineContext.logger() + + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_HTTP) + val authToken = loadAuthToken() val relativeUri = AwsSdkSetting.AwsContainerCredentialsRelativeUri.resolve(platformProvider) val fullUri = AwsSdkSetting.AwsContainerCredentialsFullUri.resolve(platformProvider) diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProvider.kt index b834fe31831..9487f95a584 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProvider.kt @@ -7,9 +7,11 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting +import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider import aws.smithy.kotlin.runtime.auth.awscredentials.simpleClassName +import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.telemetry.logging.trace import aws.smithy.kotlin.runtime.util.PlatformProvider @@ -36,6 +38,9 @@ public class EnvironmentCredentialsProvider( coroutineContext.trace { "Attempting to load credentials from env vars $ACCESS_KEY_ID/$SECRET_ACCESS_KEY/$SESSION_TOKEN" } + + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS) + return credentials( accessKeyId = requireEnv(ACCESS_KEY_ID), secretAccessKey = requireEnv(SECRET_ACCESS_KEY), diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt index a27d1343d12..7d53c2bce97 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt @@ -9,7 +9,9 @@ import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.config.imds.EC2MetadataError import aws.sdk.kotlin.runtime.config.imds.ImdsClient import aws.sdk.kotlin.runtime.config.imds.InstanceMetadataProvider +import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.* +import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.config.resolve import aws.smithy.kotlin.runtime.http.HttpStatusCode @@ -60,6 +62,8 @@ public class ImdsCredentialsProvider( private val mu = Mutex() override suspend fun resolve(attributes: Attributes): Credentials { + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_HTTP) + if (AwsSdkSetting.AwsEc2MetadataDisabled.resolve(platformProvider) == true) { throw CredentialsNotLoadedException("AWS EC2 metadata is explicitly disabled; credentials not loaded") } diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider.kt index 1334eaff933..1d9b4ad39eb 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider.kt @@ -5,10 +5,12 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials +import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException import aws.smithy.kotlin.runtime.auth.awscredentials.simpleClassName +import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.serde.json.JsonDeserializer import aws.smithy.kotlin.runtime.telemetry.logging.logger @@ -53,6 +55,8 @@ public class ProcessCredentialsProvider( override suspend fun resolve(attributes: Attributes): Credentials { val logger = coroutineContext.logger() + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS) + val (exitCode, output) = try { executeCommand(credentialProcess, platformProvider, maxOutputLengthBytes, timeoutMillis) } catch (ex: Exception) { diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt index 0fc10b27ca0..ac532d13d86 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt @@ -14,11 +14,13 @@ import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.config.imds.ImdsClient import aws.sdk.kotlin.runtime.config.profile.AwsConfigurationSource import aws.sdk.kotlin.runtime.config.profile.loadAwsSharedConfig +import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric import aws.sdk.kotlin.runtime.region.resolveRegion import aws.smithy.kotlin.runtime.auth.awscredentials.CloseableCredentialsProvider import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider import aws.smithy.kotlin.runtime.auth.awscredentials.simpleClassName +import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.http.engine.HttpClientEngine import aws.smithy.kotlin.runtime.io.closeIfCloseable @@ -125,12 +127,14 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( val profileOverride = profileName?.let { sharedConfig.profiles[it] } val region = asyncLazy { region ?: profileOverride?.getOrNull("region") ?: attributes.getOrNull(AwsClientOption.Region) ?: resolveRegion(platformProvider) } - val leaf = chain.leaf.toCredentialsProvider(region) + val leaf = chain.leaf.toCredentialsProvider(region, attributes) logger.debug { "Resolving credentials from ${chain.leaf.description()}" } var creds = leaf.resolve(attributes) chain.roles.forEach { roleArn -> logger.debug { "Assuming role `${roleArn.roleArn}`" } + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SOURCE_PROFILE) + val assumeProvider = roleArn.toCredentialsProvider(creds, region) creds = assumeProvider.resolve(attributes) } @@ -145,42 +149,60 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( } } - private suspend fun LeafProvider.toCredentialsProvider(region: LazyAsyncValue): CredentialsProvider = + private suspend fun LeafProvider.toCredentialsProvider(region: LazyAsyncValue, attributes: Attributes): CredentialsProvider = when (this) { - is LeafProvider.NamedSource -> namedProviders[name] - ?: throw ProviderConfigurationException("unknown credentials source: $name") - - is LeafProvider.AccessKey -> StaticCredentialsProvider(credentials) - - is LeafProvider.WebIdentityTokenRole -> StsWebIdentityCredentialsProvider( - roleArn, - webIdentityTokenFile, - region = region.get(), - roleSessionName = sessionName, - platformProvider = platformProvider, - httpClient = httpClient, - ) - - is LeafProvider.SsoSession -> SsoCredentialsProvider( - accountId = ssoAccountId, - roleName = ssoRoleName, - startUrl = ssoStartUrl, - ssoRegion = ssoRegion, - ssoSessionName = ssoSessionName, - httpClient = httpClient, - platformProvider = platformProvider, - ) - - is LeafProvider.LegacySso -> SsoCredentialsProvider( - accountId = ssoAccountId, - roleName = ssoRoleName, - startUrl = ssoStartUrl, - ssoRegion = ssoRegion, - httpClient = httpClient, - platformProvider = platformProvider, - ) - - is LeafProvider.Process -> ProcessCredentialsProvider(command) + is LeafProvider.NamedSource -> { + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_NAMED_PROVIDER) + namedProviders[name] + ?: throw ProviderConfigurationException("unknown credentials source: $name") + } + + is LeafProvider.AccessKey -> { + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) + StaticCredentialsProvider(credentials) + } + + is LeafProvider.WebIdentityTokenRole -> { + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN) + StsWebIdentityCredentialsProvider( + roleArn, + webIdentityTokenFile, + region = region.get(), + roleSessionName = sessionName, + platformProvider = platformProvider, + httpClient = httpClient, + ) + } + + is LeafProvider.SsoSession -> { + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO) + SsoCredentialsProvider( + accountId = ssoAccountId, + roleName = ssoRoleName, + startUrl = ssoStartUrl, + ssoRegion = ssoRegion, + ssoSessionName = ssoSessionName, + httpClient = httpClient, + platformProvider = platformProvider, + ) + } + + is LeafProvider.LegacySso -> { + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO_LEGACY) + SsoCredentialsProvider( + accountId = ssoAccountId, + roleName = ssoRoleName, + startUrl = ssoStartUrl, + ssoRegion = ssoRegion, + httpClient = httpClient, + platformProvider = platformProvider, + ) + } + + is LeafProvider.Process -> { + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_PROCESS) + ProcessCredentialsProvider(command) + } } private suspend fun RoleArn.toCredentialsProvider( diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProvider.kt index 43534179a16..9427a6f9de0 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProvider.kt @@ -8,10 +8,12 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.sso.SsoClient import aws.sdk.kotlin.runtime.auth.credentials.internal.sso.getRoleCredentials +import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException import aws.smithy.kotlin.runtime.auth.awscredentials.simpleClassName +import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.client.SdkClientOption import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.http.engine.HttpClientEngine @@ -92,9 +94,11 @@ public class SsoCredentialsProvider public constructor( val token = if (ssoTokenProvider != null) { logger.trace { "Attempting to load token using token provider for sso-session: `$ssoSessionName`" } + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_SSO) ssoTokenProvider.resolve(attributes) } else { logger.trace { "Attempting to load token from file using legacy format" } + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_SSO_LEGACY) legacyLoadTokenFile() } diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProvider.kt index 83e306e794e..2412f69e8d9 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProvider.kt @@ -13,7 +13,9 @@ import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.model.PolicyDescript import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.model.RegionDisabledException import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.model.Tag import aws.sdk.kotlin.runtime.config.AwsSdkSetting +import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.* +import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.client.SdkClientOption import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.config.resolve @@ -95,6 +97,8 @@ public class StsAssumeRoleCredentialsProvider( val logger = coroutineContext.logger() logger.debug { "retrieving assumed credentials" } + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE) + // NOTE: multi region access points require regional STS endpoints val provider = this val telemetry = coroutineContext.telemetryProvider diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt index 6a7b6056b88..19f6f0ab40f 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt @@ -11,7 +11,9 @@ import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.StsClient import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.assumeRoleWithWebIdentity import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.model.PolicyDescriptorType import aws.sdk.kotlin.runtime.config.AwsSdkSetting +import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.* +import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.client.SdkClientOption import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.config.EnvironmentSetting @@ -104,6 +106,9 @@ public class StsWebIdentityCredentialsProvider( override suspend fun resolve(attributes: Attributes): Credentials { val logger = coroutineContext.logger() logger.debug { "retrieving assumed credentials via web identity" } + + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE_WEB_ID) + val provider = this val params = provider.webIdentityParameters diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProvider.kt index 4d68fe1ce87..c5d84e42f4b 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProvider.kt @@ -7,9 +7,11 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting +import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider import aws.smithy.kotlin.runtime.auth.awscredentials.simpleClassName +import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.telemetry.logging.trace import aws.smithy.kotlin.runtime.util.PlatformProvider @@ -36,6 +38,9 @@ public class SystemPropertyCredentialsProvider( coroutineContext.trace { "Attempting to load credentials from system properties $ACCESS_KEY_ID/$SECRET_ACCESS_KEY/$SESSION_TOKEN" } + + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_JVM_SYSTEM_PROPERTIES) + return credentials( accessKeyId = requireProperty(ACCESS_KEY_ID), secretAccessKey = requireProperty(SECRET_ACCESS_KEY), diff --git a/aws-runtime/aws-http/api/aws-http.api b/aws-runtime/aws-http/api/aws-http.api index e5808232a81..3a236a76aed 100644 --- a/aws-runtime/aws-http/api/aws-http.api +++ b/aws-runtime/aws-http/api/aws-http.api @@ -148,6 +148,31 @@ public final class aws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric : public static fun values ()[Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric; } +public final class aws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials : java/lang/Enum, aws/smithy/kotlin/runtime/businessmetrics/BusinessMetric { + public static final field CREDENTIALS_CODE Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_ENV_VARS Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_HTTP Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_IMDS Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_JVM_SYSTEM_PROPERTIES Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_PROCESS Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_PROFILE Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_PROFILE_NAMED_PROVIDER Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_PROFILE_PROCESS Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_PROFILE_SOURCE_PROFILE Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_PROFILE_SSO Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_PROFILE_SSO_LEGACY Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_SSO Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_SSO_LEGACY Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_STS_ASSUME_ROLE Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_STS_ASSUME_ROLE_WEB_ID Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public fun getIdentifier ()Ljava/lang/String; + public static fun valueOf (Ljava/lang/String;)Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; + public static fun values ()[Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; +} + public final class aws/sdk/kotlin/runtime/http/interceptors/AwsSpanInterceptor : aws/smithy/kotlin/runtime/client/Interceptor { public static final field INSTANCE Laws/sdk/kotlin/runtime/http/interceptors/AwsSpanInterceptor; public fun modifyBeforeAttemptCompletion-gIAlu-s (Laws/smithy/kotlin/runtime/client/ResponseInterceptorContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptor.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptor.kt index 8a0917e05cf..6b5d96cbd8c 100644 --- a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptor.kt +++ b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptor.kt @@ -64,4 +64,26 @@ private fun formatMetrics(metrics: MutableSet): String { @InternalApi public enum class AwsBusinessMetric(public override val identifier: String) : BusinessMetric { S3_EXPRESS_BUCKET("J"), + ; + + public enum class Credentials(public override val identifier: String) : BusinessMetric { + CREDENTIALS_CODE("e"), + CREDENTIALS_JVM_SYSTEM_PROPERTIES("f"), + CREDENTIALS_ENV_VARS("g"), + CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN("h"), + CREDENTIALS_STS_ASSUME_ROLE("i"), + CREDENTIALS_STS_ASSUME_ROLE_WEB_ID("k"), + CREDENTIALS_PROFILE("n"), + CREDENTIALS_PROFILE_SOURCE_PROFILE("o"), + CREDENTIALS_PROFILE_NAMED_PROVIDER("p"), + CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN("q"), + CREDENTIALS_PROFILE_SSO("r"), + CREDENTIALS_SSO("s"), + CREDENTIALS_PROFILE_SSO_LEGACY("t"), + CREDENTIALS_SSO_LEGACY("u"), + CREDENTIALS_PROFILE_PROCESS("v"), + CREDENTIALS_PROCESS("w"), + CREDENTIALS_HTTP("z"), + CREDENTIALS_IMDS("0"), + } } diff --git a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt index 73575ddf075..ca864fb425b 100644 --- a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt +++ b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt @@ -7,6 +7,7 @@ package aws.sdk.kotlin.codegen import software.amazon.smithy.kotlin.codegen.core.KotlinWriter import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes.Auth.Signing.AwsSigningCommon.AwsSigningAttributes +import software.amazon.smithy.kotlin.codegen.core.withBlock import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration import software.amazon.smithy.kotlin.codegen.integration.SectionWriter import software.amazon.smithy.kotlin.codegen.integration.SectionWriterBinding @@ -54,7 +55,7 @@ class BusinessMetricsIntegration : KotlinIntegration { override fun customizeMiddleware( ctx: ProtocolGenerator.GenerationContext, resolved: List, - ): List = resolved + userAgentBusinessMetricsMiddleware + ): List = resolved + userAgentBusinessMetricsMiddleware + credentialsBusinessMetricsMiddleware private val userAgentBusinessMetricsMiddleware = object : ProtocolMiddleware { override val name: String = "UserAgentBusinessMetrics" @@ -65,4 +66,22 @@ class BusinessMetricsIntegration : KotlinIntegration { ) } } + + private val credentialsBusinessMetricsMiddleware = object : ProtocolMiddleware { + override val name: String = "credentialsOverrideBusinessMetricsMiddleware" + override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) { + writer.withBlock( + "if (config.credentialsProvider.#T != #S ) {", + "}", + RuntimeTypes.Auth.Credentials.AwsCredentials.simpleClassName, + "DefaultChainCredentialsProvider", + ) { + write( + "op.context.#T(#T.Credentials.CREDENTIALS_CODE)", + RuntimeTypes.Core.BusinessMetrics.emitBusinessMetric, + AwsRuntimeTypes.Http.Interceptors.AwsBusinessMetric, + ) + } + } + } } From a50e692d28dc0d9e80f0d7a9c82e572a89b57e11 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Tue, 15 Oct 2024 13:38:59 -0400 Subject: [PATCH 02/20] Profile credentials business metrics tests --- .../credentials/ProfileCredentialsProvider.kt | 5 +- .../auth/credentials/profile/ProfileChain.kt | 21 ++ .../auth/credentials/ProfileChainTest.kt | 25 +- .../ProfileCredentialsProviderTest.kt | 290 ++++++++++++++++++ 4 files changed, 328 insertions(+), 13 deletions(-) diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt index ac532d13d86..99f4232a7c4 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt @@ -9,6 +9,7 @@ import aws.sdk.kotlin.runtime.InternalSdkApi import aws.sdk.kotlin.runtime.auth.credentials.profile.LeafProvider import aws.sdk.kotlin.runtime.auth.credentials.profile.ProfileChain import aws.sdk.kotlin.runtime.auth.credentials.profile.RoleArn +import aws.sdk.kotlin.runtime.auth.credentials.profile.RoleArnSource import aws.sdk.kotlin.runtime.client.AwsClientOption import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.config.imds.ImdsClient @@ -133,7 +134,9 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( chain.roles.forEach { roleArn -> logger.debug { "Assuming role `${roleArn.roleArn}`" } - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SOURCE_PROFILE) + if (roleArn.roleArnSource == RoleArnSource.SOURCE_PROFILE) { + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SOURCE_PROFILE) + } val assumeProvider = roleArn.toCredentialsProvider(creds, region) creds = assumeProvider.resolve(attributes) diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/profile/ProfileChain.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/profile/ProfileChain.kt index a9282c87886..499330a229c 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/profile/ProfileChain.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/profile/ProfileChain.kt @@ -108,6 +108,12 @@ internal data class RoleArn( * ARN of role to assume */ val roleArn: String, + + /** + * The source used to create the [RoleArn] + */ + val roleArnSource: RoleArnSource? = null, + /** * Session name to pass to the assume role provider */ @@ -119,6 +125,14 @@ internal data class RoleArn( val externalId: String? = null, ) +/** + * Represents the possible sources for creating a [RoleArn]. + */ +internal enum class RoleArnSource { + SOURCE_PROFILE, + CREDENTIALS_SOURCE, +} + internal const val ROLE_ARN = "role_arn" internal const val EXTERNAL_ID = "external_id" internal const val ROLE_SESSION_NAME = "role_session_name" @@ -148,8 +162,15 @@ private fun AwsProfile.roleArnOrNull(): RoleArn? { val roleArn = getOrNull(ROLE_ARN) ?: return null + val roleArnSource = when { + contains(SOURCE_PROFILE) && !contains(CREDENTIAL_SOURCE) -> RoleArnSource.SOURCE_PROFILE + contains(CREDENTIAL_SOURCE) && !contains(SOURCE_PROFILE) -> RoleArnSource.CREDENTIALS_SOURCE + else -> null + } + return RoleArn( roleArn, + roleArnSource, sessionName = getOrNull(ROLE_SESSION_NAME), externalId = getOrNull(EXTERNAL_ID), ) diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileChainTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileChainTest.kt index 3dae8f5ea4e..90f1268ad8a 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileChainTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileChainTest.kt @@ -8,6 +8,7 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.profile.LeafProvider import aws.sdk.kotlin.runtime.auth.credentials.profile.ProfileChain import aws.sdk.kotlin.runtime.auth.credentials.profile.RoleArn +import aws.sdk.kotlin.runtime.auth.credentials.profile.RoleArnSource import aws.sdk.kotlin.runtime.client.AwsClientOption import aws.sdk.kotlin.runtime.config.profile.AwsConfigurationSource import aws.sdk.kotlin.runtime.config.profile.FileType @@ -51,7 +52,7 @@ class ProfileChainTest { """, chain( LeafProvider.AccessKey(Credentials("abc123", "def456")), - RoleArn("arn:aws:iam::123456789:role/RoleA"), + RoleArn("arn:aws:iam::123456789:role/RoleA", RoleArnSource.SOURCE_PROFILE), ), ), TestCase( @@ -85,7 +86,7 @@ class ProfileChainTest { """, chain( LeafProvider.AccessKey(Credentials("abc123", "def456")), - RoleArn("arn:aws:iam::123456789:role/RoleA", "my_session_name"), + RoleArn("arn:aws:iam::123456789:role/RoleA", RoleArnSource.SOURCE_PROFILE, "my_session_name"), ), ), TestCase( @@ -102,7 +103,7 @@ class ProfileChainTest { """, chain( LeafProvider.AccessKey(Credentials("abc123", "def456")), - RoleArn("arn:aws:iam::123456789:role/RoleA", externalId = "my_external_id"), + RoleArn("arn:aws:iam::123456789:role/RoleA", RoleArnSource.SOURCE_PROFILE, externalId = "my_external_id"), ), ), TestCase( @@ -127,7 +128,7 @@ class ProfileChainTest { """, chain( LeafProvider.NamedSource("Ec2InstanceMetadata"), - RoleArn("arn:aws:iam::123456789:role/RoleA"), + RoleArn("arn:aws:iam::123456789:role/RoleA", RoleArnSource.CREDENTIALS_SOURCE), ), ), TestCase( @@ -221,8 +222,8 @@ class ProfileChainTest { """, chain( LeafProvider.AccessKey(Credentials("mno456", "pqr789")), - RoleArn("arn:aws:iam::123456789:role/RoleB"), - RoleArn("arn:aws:iam::123456789:role/RoleA"), + RoleArn("arn:aws:iam::123456789:role/RoleB", RoleArnSource.SOURCE_PROFILE), + RoleArn("arn:aws:iam::123456789:role/RoleA", RoleArnSource.SOURCE_PROFILE), ), ), TestCase( @@ -245,7 +246,7 @@ class ProfileChainTest { """, chain( LeafProvider.AccessKey(Credentials("profile_b_key", "profile_b_secret")), - RoleArn("arn:aws:iam::123456789:role/RoleA"), + RoleArn("arn:aws:iam::123456789:role/RoleA", RoleArnSource.SOURCE_PROFILE), ), ), TestCase( @@ -320,7 +321,7 @@ class ProfileChainTest { """, chain( LeafProvider.WebIdentityTokenRole("arn:aws:iam::123456789:role/RoleB", "/var/token.jwt", "some_session_name"), - RoleArn("arn:aws:iam::123456789:role/RoleA"), + RoleArn("arn:aws:iam::123456789:role/RoleA", RoleArnSource.SOURCE_PROFILE), ), ), TestCase( @@ -352,7 +353,7 @@ class ProfileChainTest { """, chain( LeafProvider.LegacySso("https://d-92671207e4.awsapps.com/start", "us-east-2", "1234567", "RoleA"), - RoleArn("arn:aws:iam::123456789:role/RoleA"), + RoleArn("arn:aws:iam::123456789:role/RoleA", RoleArnSource.SOURCE_PROFILE), ), ), TestCase( @@ -434,7 +435,7 @@ class ProfileChainTest { """, chain( LeafProvider.SsoSession("my-session", "https://d-92671207e4.awsapps.com/start", "us-east-2", "1234567", "RoleA"), - RoleArn("arn:aws:iam::123456789:role/RoleA"), + RoleArn("arn:aws:iam::123456789:role/RoleA", RoleArnSource.SOURCE_PROFILE), ), ), TestCase( @@ -571,7 +572,7 @@ class ProfileChainTest { }, ), ), - RoleArn("some-arn"), + RoleArn("some-arn", RoleArnSource.SOURCE_PROFILE), ), ), TestCase( @@ -594,7 +595,7 @@ class ProfileChainTest { """, chain( LeafProvider.NamedSource("Ec2InstanceMetadata"), - RoleArn("some-arn"), + RoleArn("some-arn", RoleArnSource.CREDENTIALS_SOURCE), ), ), TestCase( diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt index dd26b335337..39540306d82 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt @@ -7,15 +7,23 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.client.AwsClientOption +import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials +import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException +import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics import aws.smithy.kotlin.runtime.collections.attributesOf +import aws.smithy.kotlin.runtime.collections.get import aws.smithy.kotlin.runtime.httptest.TestConnection import aws.smithy.kotlin.runtime.httptest.buildTestConnection import aws.smithy.kotlin.runtime.net.Host +import aws.smithy.kotlin.runtime.operation.ExecutionContext import aws.smithy.kotlin.runtime.util.TestPlatformProvider +import io.mockk.coEvery +import io.mockk.mockkStatic import kotlinx.coroutines.test.runTest import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertTrue class ProfileCredentialsProviderTest { @Test @@ -318,4 +326,286 @@ class ProfileCredentialsProviderTest { val expected = credentials("AKID-Default", "Default-Secret", accountId = "12345") assertEquals(expected, actual) } + + @Test + fun staticCredentialsBusinessMetrics() = runTest { + val testProvider = TestPlatformProvider( + env = mapOf("AWS_CONFIG_FILE" to "config"), + fs = mapOf( + "config" to """ + [default] + aws_access_key_id = AKID-Default + aws_secret_access_key = Default-Secret + """.trimIndent(), + ), + ) + val testEngine = TestConnection() + + val provider = ProfileCredentialsProvider( + platformProvider = testProvider, + httpClient = testEngine, + ) + val attributes = ExecutionContext() + provider.resolve(attributes) + + assertTrue(attributes.contains(BusinessMetrics)) + + val actual = attributes[BusinessMetrics] + val expected = setOf( + AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE.identifier, + ) + assertEquals(expected, actual) + } + + @Test + fun assumeRoleWithSourceProfileBusinessMetrics() = runTest { + val testArn = "arn:aws:iam::1234567:role/test-role" + val testProvider = TestPlatformProvider( + env = mapOf( + "AWS_CONFIG_FILE" to "config", + "AWS_REGION" to "us-west-2", + ), + fs = mapOf( + "config" to """ + [default] + role_arn = $testArn + source_profile = B + + [profile B] + region = us-east-1 + aws_access_key_id = AKID-Profile + aws_secret_access_key = Profile-Secret + """.trimIndent(), + ), + ) + val testEngine = buildTestConnection { + expect(StsTestUtils.stsResponse(testArn)) + } + + val provider = ProfileCredentialsProvider( + platformProvider = testProvider, + httpClient = testEngine, + ) + val attributes = ExecutionContext() + provider.resolve(attributes) + + assertTrue(attributes.contains(BusinessMetrics)) + + val actual = attributes[BusinessMetrics] + val expected = setOf( + AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SOURCE_PROFILE.identifier, + AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE.identifier, + AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE.identifier, + ) + assertEquals(expected, actual) + } + + @Test + fun assumeRoleWithNamedProviderBusinessMetrics() = runTest { + val testArn = "arn:aws:iam::1234567:role/test-role" + val testProvider = TestPlatformProvider( + env = mapOf( + "AWS_CONFIG_FILE" to "config", + "AWS_REGION" to "us-west-2", + "AWS_ACCESS_KEY_ID" to "1", + "AWS_SECRET_ACCESS_KEY" to "2", + ), + fs = mapOf( + "config" to """ + [default] + role_arn = $testArn + credential_source = Environment + """.trimIndent(), + ), + ) + val testEngine = buildTestConnection { + expect(StsTestUtils.stsResponse(testArn)) + } + + val provider = ProfileCredentialsProvider( + platformProvider = testProvider, + httpClient = testEngine, + ) + val attributes = ExecutionContext() + provider.resolve(attributes) + + assertTrue(attributes.contains(BusinessMetrics)) + + val actual = attributes[BusinessMetrics] + val expected = setOf( + AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_NAMED_PROVIDER.identifier, + AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS.identifier, + AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE.identifier, + ) + assertEquals(expected, actual) + } + + @Test + fun assumeRoleWithWebIdentityTokenBusinessMetrics() = runTest { + val testArn = "arn:aws:iam::1234567:role/test-role" + val testProvider = TestPlatformProvider( + env = mapOf( + "AWS_CONFIG_FILE" to "config", + "AWS_REGION" to "us-west-2", + ), + fs = mapOf( + "config" to """ + [default] + role_arn = $testArn + web_identity_token_file = /some/path/to/test-token + """.trimIndent(), + "/some/path/to/test-token" to "token", + ), + ) + val testEngine = buildTestConnection { + expect(StsTestUtils.stsRequest(emptyMap()), StsTestUtils.stsResponse(testArn)) + } + + val provider = ProfileCredentialsProvider( + platformProvider = testProvider, + httpClient = testEngine, + ) + val attributes = ExecutionContext() + try { + provider.resolve(attributes) + } catch (e: CredentialsProviderException) { + if (e.message != "STS failed to assume role from web identity") throw e + } + + assertTrue(attributes.contains(BusinessMetrics)) + + val actual = attributes[BusinessMetrics] + val expected = setOf( + AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN.identifier, + AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE_WEB_ID.identifier, + ) + assertEquals(expected, actual) + } + + @Test + fun ssoBusinessMetrics() = runTest { + val testProvider = TestPlatformProvider( + env = mapOf( + "AWS_CONFIG_FILE" to "config", + ), + fs = mapOf( + "config" to """ + [default] + sso_session = B + sso_account_id = 012345678901 + sso_role_name = SampleRole + + [sso-session B] + sso_region = us-east-1 + sso_start_url = https://d-abc123.awsapps.com/start + """.trimIndent(), + ), + ) + val testEngine = TestConnection() + val provider = ProfileCredentialsProvider( + platformProvider = testProvider, + httpClient = testEngine, + ) + val attributes = ExecutionContext() + try { + provider.resolve(attributes) + } catch (e: ProviderConfigurationException) { + if (e.message != "Invalid or missing SSO session cache. Run `aws sso login` to initiate a new SSO session") throw e + } + + assertTrue(attributes.contains(BusinessMetrics)) + + val actual = attributes[BusinessMetrics] + val expected = setOf( + AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO.identifier, + AwsBusinessMetric.Credentials.CREDENTIALS_SSO.identifier, + ) + assertEquals(expected, actual) + } + + @Test + fun legacySsoBusinessMetrics() = runTest { + val testProvider = TestPlatformProvider( + env = mapOf( + "AWS_CONFIG_FILE" to "config", + ), + fs = mapOf( + "config" to """ + [default] + sso_account_id = 012345678901 + sso_region = us-east-1 + sso_role_name = SampleRole + sso_start_url = https://d-abc123.awsapps.com/start-beta + """.trimIndent(), + ), + ) + val testEngine = TestConnection() + val provider = ProfileCredentialsProvider( + platformProvider = testProvider, + httpClient = testEngine, + ) + val attributes = ExecutionContext() + try { + provider.resolve(attributes) + } catch (e: ProviderConfigurationException) { + if (e.message != "Invalid or missing SSO session cache. Run `aws sso login` to initiate a new SSO session") throw e + } + + assertTrue(attributes.contains(BusinessMetrics)) + + val actual = attributes[BusinessMetrics] + val expected = setOf( + AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO_LEGACY.identifier, + AwsBusinessMetric.Credentials.CREDENTIALS_SSO_LEGACY.identifier, + ) + assertEquals(expected, actual) + } + + @Test + fun processBusinessMetrics() = runTest { + val testProvider = TestPlatformProvider( + env = mapOf( + "AWS_CONFIG_FILE" to "config", + ), + fs = mapOf( + "config" to """ + [default] + credential_process = awscreds-custom + """.trimIndent(), + "awscreds-custom" to "some-process", + ), + ) + val testEngine = TestConnection() + val provider = ProfileCredentialsProvider( + platformProvider = testProvider, + httpClient = testEngine, + ) + + mockkStatic(::executeCommand) + coEvery { executeCommand(any(), any(), any(), any(), any()) }.returns( + Pair( + 0, + """ + { + "Version": 1, + "AccessKeyId": "AccessKeyId", + "SecretAccessKey": "SecretAccessKey", + "SessionToken": "SessionToken" + } + """.trimIndent(), + ), + ) + + val attributes = ExecutionContext() + provider.resolve(attributes) + + assertTrue(attributes.contains(BusinessMetrics)) + + val actual = attributes[BusinessMetrics] + val expected = setOf( + AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_PROCESS.identifier, + AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS.identifier, + ) + assertEquals(expected, actual) + } } From 8d61e07ee8770d09792a8839af7203d0db319753 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Tue, 15 Oct 2024 17:20:48 -0400 Subject: [PATCH 03/20] Bug fixes around business metrics --- .../auth/credentials/DefaultChainCredentialsProvider.kt | 3 ++- .../aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt index 79f00909b82..4547710610b 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt @@ -12,6 +12,7 @@ import aws.smithy.kotlin.runtime.auth.awscredentials.* import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.collections.MutableAttributes +import aws.smithy.kotlin.runtime.collections.get import aws.smithy.kotlin.runtime.collections.merge import aws.smithy.kotlin.runtime.http.engine.DefaultHttpEngine import aws.smithy.kotlin.runtime.http.engine.HttpClientEngine @@ -81,7 +82,7 @@ public class DefaultChainCredentialsProvider constructor( private val provider = CachedCredentialsProvider(chain) override suspend fun resolve(attributes: Attributes): Credentials { - if (attributes is MutableAttributes) (this as MutableAttributes).merge(executionContext) + if (attributes is MutableAttributes) attributes.merge(executionContext) return provider.resolve(attributes) } diff --git a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt index ca864fb425b..e5168d2de08 100644 --- a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt +++ b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt @@ -74,7 +74,13 @@ class BusinessMetricsIntegration : KotlinIntegration { "if (config.credentialsProvider.#T != #S ) {", "}", RuntimeTypes.Auth.Credentials.AwsCredentials.simpleClassName, - "DefaultChainCredentialsProvider", + /** + * If a [CredentialsProvider] is not wrapped by [aws.sdk.kotlin.runtime.auth.credentials.internal.ManagedCredentialsProvider] + * it means that it was not created by the SDK, suggesting that the user has supplied their own [CredentialsProvider]. + * + * See:[AwsServiceConfigIntegration.CredentialsProviderProp] + */ + "ManagedCredentialsProvider", ) { write( "op.context.#T(#T.Credentials.CREDENTIALS_CODE)", From 18c80e780be28756376944d12f0b78c0122e9d3f Mon Sep 17 00:00:00 2001 From: 0marperez Date: Wed, 16 Oct 2024 10:17:30 -0400 Subject: [PATCH 04/20] Added business metrics to default chain credentials provider tests --- aws-runtime/aws-config/api/aws-config.api | 1 + .../DefaultChainCredentialsProvider.kt | 12 +--- .../credentials/ImdsCredentialsProvider.kt | 2 +- .../StsWebIdentityCredentialsProvider.kt | 58 ++++++++++++++++++- .../ecs_assume_role/test-case.json | 3 +- .../ecs_credentials/test-case.json | 3 +- .../imds_assume_role/test-case.json | 3 +- .../imds_config_with_no_creds/test-case.json | 3 +- .../imds_default_chain_retries/test-case.json | 3 +- .../imds_default_chain_success/test-case.json | 3 +- .../legacy_sso_role/test-case.json | 3 +- .../prefer_environment/test-case.json | 3 +- .../prefer_system_properties/test-case.json | 3 +- .../profile_name/test-case.json | 3 +- .../profile_static_keys/test-case.json | 3 +- .../retry_on_error/test-case.json | 3 +- .../sso_session/test-case.json | 3 +- .../web_identity_token_env/test-case.json | 3 +- .../web_identity_token_profile/test-case.json | 3 +- .../test-case.json | 3 +- .../DefaultChainCredentialsProviderTest.kt | 19 ++++-- 21 files changed, 105 insertions(+), 35 deletions(-) diff --git a/aws-runtime/aws-config/api/aws-config.api b/aws-runtime/aws-config/api/aws-config.api index 15c22b39604..db7971c9168 100644 --- a/aws-runtime/aws-config/api/aws-config.api +++ b/aws-runtime/aws-config/api/aws-config.api @@ -203,6 +203,7 @@ public final class aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredent public synthetic fun (Laws/sdk/kotlin/runtime/auth/credentials/AssumeRoleWithWebIdentityParameters;Ljava/lang/String;Laws/smithy/kotlin/runtime/util/PlatformProvider;Laws/smithy/kotlin/runtime/http/engine/HttpClientEngine;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JLaws/smithy/kotlin/runtime/util/PlatformProvider;Laws/smithy/kotlin/runtime/http/engine/HttpClientEngine;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JLaws/smithy/kotlin/runtime/util/PlatformProvider;Laws/smithy/kotlin/runtime/http/engine/HttpClientEngine;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JLaws/smithy/kotlin/runtime/util/PlatformProvider;Laws/smithy/kotlin/runtime/http/engine/HttpClientEngine;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getHttpClient ()Laws/smithy/kotlin/runtime/http/engine/HttpClientEngine; public final fun getPlatformProvider ()Laws/smithy/kotlin/runtime/util/PlatformProvider; public final fun getRegion ()Ljava/lang/String; diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt index 4547710610b..3685f95b02a 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt @@ -7,18 +7,13 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.config.imds.ImdsClient -import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.* -import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.Attributes -import aws.smithy.kotlin.runtime.collections.MutableAttributes import aws.smithy.kotlin.runtime.collections.get -import aws.smithy.kotlin.runtime.collections.merge import aws.smithy.kotlin.runtime.http.engine.DefaultHttpEngine import aws.smithy.kotlin.runtime.http.engine.HttpClientEngine import aws.smithy.kotlin.runtime.io.Closeable import aws.smithy.kotlin.runtime.io.closeIfCloseable -import aws.smithy.kotlin.runtime.operation.ExecutionContext import aws.smithy.kotlin.runtime.util.PlatformProvider /** @@ -51,7 +46,6 @@ public class DefaultChainCredentialsProvider constructor( httpClient: HttpClientEngine? = null, public val region: String? = null, ) : CloseableCredentialsProvider { - private val executionContext = ExecutionContext() private val manageEngine = httpClient == null private val engine = httpClient ?: DefaultHttpEngine() @@ -59,7 +53,6 @@ public class DefaultChainCredentialsProvider constructor( SystemPropertyCredentialsProvider(platformProvider::getProperty), EnvironmentCredentialsProvider(platformProvider::getenv), LazilyInitializedCredentialsProvider("EnvironmentStsWebIdentityCredentialsProvider") { - executionContext.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN) StsWebIdentityCredentialsProvider.fromEnvironment( platformProvider = platformProvider, httpClient = httpClient, @@ -81,10 +74,7 @@ public class DefaultChainCredentialsProvider constructor( private val provider = CachedCredentialsProvider(chain) - override suspend fun resolve(attributes: Attributes): Credentials { - if (attributes is MutableAttributes) attributes.merge(executionContext) - return provider.resolve(attributes) - } + override suspend fun resolve(attributes: Attributes): Credentials = provider.resolve(attributes) override fun close() { provider.close() diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt index 7d53c2bce97..824432cb712 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt @@ -62,7 +62,7 @@ public class ImdsCredentialsProvider( private val mu = Mutex() override suspend fun resolve(attributes: Attributes): Credentials { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_HTTP) + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) if (AwsSdkSetting.AwsEc2MetadataDisabled.resolve(platformProvider) == true) { throw CredentialsNotLoadedException("AWS EC2 metadata is explicitly disabled; credentials not loaded") diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt index 19f6f0ab40f..0959d885f94 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt @@ -44,6 +44,10 @@ public class StsWebIdentityCredentialsProvider( public val platformProvider: PlatformProvider = PlatformProvider.System, public val httpClient: HttpClientEngine? = null, ) : CredentialsProvider { + /** + * Indicates if the class was created using [fromEnvironment] + */ + private var createdFromEnvironment: Boolean = false /** * A [CredentialsProvider] that exchanges a Web Identity Token for credentials from the AWS Security Token Service @@ -81,6 +85,46 @@ public class StsWebIdentityCredentialsProvider( httpClient, ) + /** + * A [CredentialsProvider] that exchanges a Web Identity Token for credentials from the AWS Security Token Service + * (STS). + * + * @param roleArn The ARN of the target role to assume, e.g. `arn:aws:iam:123456789:role/example` + * @param webIdentityTokenFilePath The path to the file containing a JWT token + * @param region The AWS region to assume the role in + * @param roleSessionName The name to associate with the session. Use the role session name to uniquely identify a + * session when the same role is assumed by different principals or for different reasons. In cross-account + * scenarios, the role session name is visible to, and can be logged by the account that owns the role. The role + * session name is also in the ARN of the assumed role principal. + * @param duration The expiry duration of the credentials. Defaults to 15 minutes if not set. + * @param platformProvider The platform API provider + * @param httpClient the [HttpClientEngine] instance to use to make requests. NOTE: This engine's resources and + * lifetime are NOT managed by the provider. Caller is responsible for closing. + * @param createdFromEnvironment If the [StsWebIdentityCredentialsProvider] was created using [fromEnvironment]. + */ + private constructor( + roleArn: String, + webIdentityTokenFilePath: String, + region: String?, + roleSessionName: String? = null, + duration: Duration = DEFAULT_CREDENTIALS_REFRESH_SECONDS.seconds, + platformProvider: PlatformProvider = PlatformProvider.System, + httpClient: HttpClientEngine? = null, + createdFromEnvironment: Boolean, + ) : this( + AssumeRoleWithWebIdentityParameters( + roleArn = roleArn, + webIdentityTokenFilePath = webIdentityTokenFilePath, + roleSessionName = roleSessionName, + duration = duration, + ), + region, + platformProvider, + httpClient, + ) { + this.createdFromEnvironment = createdFromEnvironment + } + public companion object { /** * Create an [StsWebIdentityCredentialsProvider] from the current execution environment. This will attempt @@ -99,7 +143,16 @@ public class StsWebIdentityCredentialsProvider( val resolvedRoleArn = platformProvider.resolve(roleArn, AwsSdkSetting.AwsRoleArn, "roleArn") val resolvedTokenFilePath = platformProvider.resolve(webIdentityTokenFilePath, AwsSdkSetting.AwsWebIdentityTokenFile, "webIdentityTokenFilePath") val resolvedRegion = region ?: AwsSdkSetting.AwsRegion.resolve(platformProvider) - return StsWebIdentityCredentialsProvider(resolvedRoleArn, resolvedTokenFilePath, resolvedRegion, roleSessionName, duration, platformProvider, httpClient) + return StsWebIdentityCredentialsProvider( + resolvedRoleArn, + resolvedTokenFilePath, + resolvedRegion, + roleSessionName, + duration, + platformProvider, + httpClient, + createdFromEnvironment = true, + ) } } @@ -107,6 +160,9 @@ public class StsWebIdentityCredentialsProvider( val logger = coroutineContext.logger() logger.debug { "retrieving assumed credentials via web identity" } + if (createdFromEnvironment) { + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN) + } attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE_WEB_ID) val provider = this diff --git a/aws-runtime/aws-config/common/test-resources/default-provider-chain/ecs_assume_role/test-case.json b/aws-runtime/aws-config/common/test-resources/default-provider-chain/ecs_assume_role/test-case.json index 34730c3ab04..e2c9a8dd33f 100644 --- a/aws-runtime/aws-config/common/test-resources/default-provider-chain/ecs_assume_role/test-case.json +++ b/aws-runtime/aws-config/common/test-resources/default-provider-chain/ecs_assume_role/test-case.json @@ -7,7 +7,8 @@ "secret_access_key": "secretkeycorrect", "session_token": "tokencorrect", "expiry": 1632249686, - "accountId": "130633740322" + "accountId": "130633740322", + "business_metrics": ["p","z", "i"] } } } diff --git a/aws-runtime/aws-config/common/test-resources/default-provider-chain/ecs_credentials/test-case.json b/aws-runtime/aws-config/common/test-resources/default-provider-chain/ecs_credentials/test-case.json index a401eea0aa4..de4693e51b6 100644 --- a/aws-runtime/aws-config/common/test-resources/default-provider-chain/ecs_credentials/test-case.json +++ b/aws-runtime/aws-config/common/test-resources/default-provider-chain/ecs_credentials/test-case.json @@ -6,7 +6,8 @@ "access_key_id": "ASIARCORRECT", "secret_access_key": "secretkeycorrect", "session_token": "tokencorrect", - "expiry": 1234567890 + "expiry": 1234567890, + "business_metrics": ["z"] } } } diff --git a/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_assume_role/test-case.json b/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_assume_role/test-case.json index 8f59ddf5c61..8d53f734c01 100644 --- a/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_assume_role/test-case.json +++ b/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_assume_role/test-case.json @@ -7,7 +7,8 @@ "secret_access_key": "secretkeycorrect", "session_token": "tokencorrect", "expiry": 1632249686, - "accountId": "130633740322" + "accountId": "130633740322", + "business_metrics": ["p","0", "i"] } } } diff --git a/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_config_with_no_creds/test-case.json b/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_config_with_no_creds/test-case.json index 73a9fe8bb21..988fb6690a4 100644 --- a/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_config_with_no_creds/test-case.json +++ b/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_config_with_no_creds/test-case.json @@ -6,7 +6,8 @@ "access_key_id": "ASIARTEST", "secret_access_key": "testsecret", "session_token": "testtoken", - "expiry": 1632197813 + "expiry": 1632197813, + "business_metrics": ["0"] } } } diff --git a/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_default_chain_retries/test-case.json b/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_default_chain_retries/test-case.json index b8048ebbf63..c5c57a67115 100644 --- a/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_default_chain_retries/test-case.json +++ b/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_default_chain_retries/test-case.json @@ -6,7 +6,8 @@ "access_key_id": "ASIARTEST", "secret_access_key": "testsecret", "session_token": "testtoken", - "expiry": 1632270906 + "expiry": 1632270906, + "business_metrics": ["0"] } } } diff --git a/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_default_chain_success/test-case.json b/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_default_chain_success/test-case.json index f951f70eea8..63ebc8b3118 100644 --- a/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_default_chain_success/test-case.json +++ b/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_default_chain_success/test-case.json @@ -6,7 +6,8 @@ "access_key_id": "ASIARTEST", "secret_access_key": "testsecret", "session_token": "testtoken", - "expiry": 1632197813 + "expiry": 1632197813, + "business_metrics": ["0"] } } } diff --git a/aws-runtime/aws-config/common/test-resources/default-provider-chain/legacy_sso_role/test-case.json b/aws-runtime/aws-config/common/test-resources/default-provider-chain/legacy_sso_role/test-case.json index 2406302d49c..083df326798 100644 --- a/aws-runtime/aws-config/common/test-resources/default-provider-chain/legacy_sso_role/test-case.json +++ b/aws-runtime/aws-config/common/test-resources/default-provider-chain/legacy_sso_role/test-case.json @@ -7,7 +7,8 @@ "secret_access_key": "secretkeycorrect", "session_token": "tokencorrect", "expiry": 1641240833, - "accountId": "123456789" + "accountId": "123456789", + "business_metrics": ["t","u"] } } } diff --git a/aws-runtime/aws-config/common/test-resources/default-provider-chain/prefer_environment/test-case.json b/aws-runtime/aws-config/common/test-resources/default-provider-chain/prefer_environment/test-case.json index 97751f5ef4e..5763fe0e8dc 100644 --- a/aws-runtime/aws-config/common/test-resources/default-provider-chain/prefer_environment/test-case.json +++ b/aws-runtime/aws-config/common/test-resources/default-provider-chain/prefer_environment/test-case.json @@ -4,7 +4,8 @@ "result": { "Ok": { "access_key_id": "correct_key", - "secret_access_key": "correct_secret" + "secret_access_key": "correct_secret", + "business_metrics": ["g"] } } } diff --git a/aws-runtime/aws-config/common/test-resources/default-provider-chain/prefer_system_properties/test-case.json b/aws-runtime/aws-config/common/test-resources/default-provider-chain/prefer_system_properties/test-case.json index 8a0f7984a63..5a0dffa6423 100644 --- a/aws-runtime/aws-config/common/test-resources/default-provider-chain/prefer_system_properties/test-case.json +++ b/aws-runtime/aws-config/common/test-resources/default-provider-chain/prefer_system_properties/test-case.json @@ -4,7 +4,8 @@ "result": { "Ok": { "access_key_id": "correct_key", - "secret_access_key": "correct_secret" + "secret_access_key": "correct_secret", + "business_metrics": ["f"] } } } \ No newline at end of file diff --git a/aws-runtime/aws-config/common/test-resources/default-provider-chain/profile_name/test-case.json b/aws-runtime/aws-config/common/test-resources/default-provider-chain/profile_name/test-case.json index f7a1a28b8b8..5d4250229df 100644 --- a/aws-runtime/aws-config/common/test-resources/default-provider-chain/profile_name/test-case.json +++ b/aws-runtime/aws-config/common/test-resources/default-provider-chain/profile_name/test-case.json @@ -4,7 +4,8 @@ "result": { "Ok": { "access_key_id": "correct_key", - "secret_access_key": "correct_secret" + "secret_access_key": "correct_secret", + "business_metrics": ["n"] } } } diff --git a/aws-runtime/aws-config/common/test-resources/default-provider-chain/profile_static_keys/test-case.json b/aws-runtime/aws-config/common/test-resources/default-provider-chain/profile_static_keys/test-case.json index c56a02bac65..10d9c849225 100644 --- a/aws-runtime/aws-config/common/test-resources/default-provider-chain/profile_static_keys/test-case.json +++ b/aws-runtime/aws-config/common/test-resources/default-provider-chain/profile_static_keys/test-case.json @@ -4,7 +4,8 @@ "result": { "Ok": { "access_key_id": "correct_key", - "secret_access_key": "correct_secret" + "secret_access_key": "correct_secret", + "business_metrics": ["n"] } } } diff --git a/aws-runtime/aws-config/common/test-resources/default-provider-chain/retry_on_error/test-case.json b/aws-runtime/aws-config/common/test-resources/default-provider-chain/retry_on_error/test-case.json index 0eccf448608..dc73d64080d 100644 --- a/aws-runtime/aws-config/common/test-resources/default-provider-chain/retry_on_error/test-case.json +++ b/aws-runtime/aws-config/common/test-resources/default-provider-chain/retry_on_error/test-case.json @@ -7,7 +7,8 @@ "secret_access_key": "TESTSECRETKEY", "session_token": "TESTSESSIONTOKEN", "expiry": 1628193482, - "accountId": "123456789012" + "accountId": "123456789012", + "business_metrics": ["o","n", "i"] } } } diff --git a/aws-runtime/aws-config/common/test-resources/default-provider-chain/sso_session/test-case.json b/aws-runtime/aws-config/common/test-resources/default-provider-chain/sso_session/test-case.json index fdb622b1ff8..462c1fae0ba 100644 --- a/aws-runtime/aws-config/common/test-resources/default-provider-chain/sso_session/test-case.json +++ b/aws-runtime/aws-config/common/test-resources/default-provider-chain/sso_session/test-case.json @@ -7,7 +7,8 @@ "secret_access_key": "secretkeycorrect", "session_token": "tokencorrect", "expiry": 1641240833, - "accountId": "123456789" + "accountId": "123456789", + "business_metrics": ["r","s"] } } } diff --git a/aws-runtime/aws-config/common/test-resources/default-provider-chain/web_identity_token_env/test-case.json b/aws-runtime/aws-config/common/test-resources/default-provider-chain/web_identity_token_env/test-case.json index 511e780ad17..ce71bbc5209 100644 --- a/aws-runtime/aws-config/common/test-resources/default-provider-chain/web_identity_token_env/test-case.json +++ b/aws-runtime/aws-config/common/test-resources/default-provider-chain/web_identity_token_env/test-case.json @@ -7,7 +7,8 @@ "secret_access_key": "SECRETKEYTEST", "session_token": "SESSIONTOKEN_TEST", "expiry": 1629147173, - "accountId": "123456789012" + "accountId": "123456789012", + "business_metrics": ["h", "k"] } } } diff --git a/aws-runtime/aws-config/common/test-resources/default-provider-chain/web_identity_token_profile/test-case.json b/aws-runtime/aws-config/common/test-resources/default-provider-chain/web_identity_token_profile/test-case.json index 3d18b175471..939cdf9afcd 100644 --- a/aws-runtime/aws-config/common/test-resources/default-provider-chain/web_identity_token_profile/test-case.json +++ b/aws-runtime/aws-config/common/test-resources/default-provider-chain/web_identity_token_profile/test-case.json @@ -7,7 +7,8 @@ "secret_access_key": "TESTSECRET", "session_token": "TESTSESSIONTOKEN", "expiry": 1629233704, - "accountId": "123456789012" + "accountId": "123456789012", + "business_metrics": ["q", "k"] } } } diff --git a/aws-runtime/aws-config/common/test-resources/default-provider-chain/web_identity_token_source_profile/test-case.json b/aws-runtime/aws-config/common/test-resources/default-provider-chain/web_identity_token_source_profile/test-case.json index b1b0ea1e38b..22ade3a6fea 100644 --- a/aws-runtime/aws-config/common/test-resources/default-provider-chain/web_identity_token_source_profile/test-case.json +++ b/aws-runtime/aws-config/common/test-resources/default-provider-chain/web_identity_token_source_profile/test-case.json @@ -7,7 +7,8 @@ "secret_access_key": "TESTSECRETKEY", "session_token": "TESTSESSIONTOKEN", "expiry": 1628193482, - "accountId": "123456789012" + "accountId": "123456789012", + "business_metrics": ["o","q", "k", "i"] } } } diff --git a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt index 93bddd342f4..77927fdcb30 100644 --- a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt @@ -8,7 +8,10 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.copy +import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics +import aws.smithy.kotlin.runtime.collections.get import aws.smithy.kotlin.runtime.httptest.TestConnection +import aws.smithy.kotlin.runtime.operation.ExecutionContext import aws.smithy.kotlin.runtime.time.Instant import aws.smithy.kotlin.runtime.util.Filesystem import aws.smithy.kotlin.runtime.util.OperatingSystem @@ -17,10 +20,7 @@ import aws.smithy.kotlin.runtime.util.PlatformProvider import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.test.runTest import kotlinx.coroutines.withContext -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive -import kotlinx.serialization.json.longOrNull +import kotlinx.serialization.json.* import java.io.File import java.nio.file.Paths import kotlin.test.Test @@ -79,6 +79,7 @@ class DefaultChainCredentialsProviderTest { override val name: String, override val docs: String, val creds: Credentials, + val businessMetrics: MutableSet, ) : TestResult() data class ErrorContains( @@ -103,7 +104,8 @@ class DefaultChainCredentialsProviderTest { o["expiry"]?.jsonPrimitive?.longOrNull?.let { Instant.fromEpochSeconds(it) }, accountId = o["accountId"]?.jsonPrimitive?.content, ) - Ok(name, docs, creds) + val businessMetrics = o["business_metrics"]?.jsonArray?.map { it.jsonPrimitive.content }?.toMutableSet() ?: mutableSetOf() + Ok(name, docs, creds, businessMetrics) } "ErrorContains" in result -> ErrorContains(name, docs, checkNotNull(result["ErrorContains"]).jsonPrimitive.content) else -> error("unrecognized result object: $result") @@ -166,7 +168,8 @@ class DefaultChainCredentialsProviderTest { fun executeTest(name: String) = runTest { val test = makeTest(name) val provider = DefaultChainCredentialsProvider(platformProvider = test.testPlatform, httpClient = test.testEngine) - val actual = runCatching { provider.resolve() } + val attributes = ExecutionContext() + val actual = runCatching { provider.resolve(attributes) } val expected = test.expected when { expected is TestResult.Ok && actual.isFailure -> error("expected success, got error: $actual") @@ -180,6 +183,10 @@ class DefaultChainCredentialsProviderTest { val creds = actualCreds.copy(providerName = null, expiration = sanitizedExpiration) assertEquals(expected.creds, creds) + if (expected.businessMetrics.isNotEmpty()) { + assertEquals(expected.businessMetrics, attributes[BusinessMetrics]) // TODO: FILL OUT TESTS + } + // assert http traffic to the extent we can. These tests do not have specific timestamps they // were signed with and some lack enough context to even assert a body (e.g. incorrect content-type). // They would require additional metadata to make use of `testEngine.assertRequests()`. From 8244764113bac8a7d10b68257fad000f942b7d40 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Wed, 16 Oct 2024 10:31:41 -0400 Subject: [PATCH 05/20] Self review --- .../runtime/auth/credentials/DefaultChainCredentialsProvider.kt | 2 ++ .../auth/credentials/DefaultChainCredentialsProviderTest.kt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt index 3685f95b02a..2aed4c42198 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt @@ -46,6 +46,7 @@ public class DefaultChainCredentialsProvider constructor( httpClient: HttpClientEngine? = null, public val region: String? = null, ) : CloseableCredentialsProvider { + private val manageEngine = httpClient == null private val engine = httpClient ?: DefaultHttpEngine() @@ -53,6 +54,7 @@ public class DefaultChainCredentialsProvider constructor( SystemPropertyCredentialsProvider(platformProvider::getProperty), EnvironmentCredentialsProvider(platformProvider::getenv), LazilyInitializedCredentialsProvider("EnvironmentStsWebIdentityCredentialsProvider") { + // STS web identity provider can be constructed from either the profile OR 100% from the environment StsWebIdentityCredentialsProvider.fromEnvironment( platformProvider = platformProvider, httpClient = httpClient, diff --git a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt index 77927fdcb30..4cc812fc4f8 100644 --- a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt @@ -184,7 +184,7 @@ class DefaultChainCredentialsProviderTest { assertEquals(expected.creds, creds) if (expected.businessMetrics.isNotEmpty()) { - assertEquals(expected.businessMetrics, attributes[BusinessMetrics]) // TODO: FILL OUT TESTS + assertEquals(expected.businessMetrics, attributes[BusinessMetrics]) } // assert http traffic to the extent we can. These tests do not have specific timestamps they From 9c3967c9f7b92bb59c6b2ec1ca76f7b3f731980d Mon Sep 17 00:00:00 2001 From: 0marperez Date: Wed, 16 Oct 2024 12:35:35 -0400 Subject: [PATCH 06/20] Allow running protocol tests with snapshot smithy kotlin versions --- codegen/protocol-tests/build.gradle.kts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/codegen/protocol-tests/build.gradle.kts b/codegen/protocol-tests/build.gradle.kts index f767e24df20..7064d116b31 100644 --- a/codegen/protocol-tests/build.gradle.kts +++ b/codegen/protocol-tests/build.gradle.kts @@ -73,14 +73,6 @@ dependencies { codegen(libs.smithy.aws.protocol.tests) } -tasks.generateSmithyProjections { - // ensure the generated clients use the same version of the runtime as the aws aws-runtime - val smithyKotlinRuntimeVersion = libs.versions.smithy.kotlin.runtime.version.get() - doFirst { - System.setProperty("smithy.kotlin.codegen.clientRuntimeVersion", smithyKotlinRuntimeVersion) - } -} - abstract class ProtocolTestTask @Inject constructor(private val project: Project) : DefaultTask() { /** * The projection From 92e091e66c1e4921d4967409e92bd2f562148067 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Wed, 16 Oct 2024 13:09:14 -0400 Subject: [PATCH 07/20] Revert previous commit --- codegen/protocol-tests/build.gradle.kts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/codegen/protocol-tests/build.gradle.kts b/codegen/protocol-tests/build.gradle.kts index 7064d116b31..f767e24df20 100644 --- a/codegen/protocol-tests/build.gradle.kts +++ b/codegen/protocol-tests/build.gradle.kts @@ -73,6 +73,14 @@ dependencies { codegen(libs.smithy.aws.protocol.tests) } +tasks.generateSmithyProjections { + // ensure the generated clients use the same version of the runtime as the aws aws-runtime + val smithyKotlinRuntimeVersion = libs.versions.smithy.kotlin.runtime.version.get() + doFirst { + System.setProperty("smithy.kotlin.codegen.clientRuntimeVersion", smithyKotlinRuntimeVersion) + } +} + abstract class ProtocolTestTask @Inject constructor(private val project: Project) : DefaultTask() { /** * The projection From a4e5eca83957db6b8ace608b236326054776f050 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 17 Oct 2024 11:02:11 -0400 Subject: [PATCH 08/20] Naming changes --- .../runtime/auth/credentials/ProfileCredentialsProvider.kt | 2 +- .../sdk/kotlin/runtime/auth/credentials/profile/ProfileChain.kt | 2 +- .../default-provider-chain/imds_assume_role/test-case.json | 2 +- .../runtime/auth/credentials/ProfileCredentialsProviderTest.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt index 99f4232a7c4..6d7aa1e4fe8 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt @@ -134,7 +134,7 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( chain.roles.forEach { roleArn -> logger.debug { "Assuming role `${roleArn.roleArn}`" } - if (roleArn.roleArnSource == RoleArnSource.SOURCE_PROFILE) { + if (roleArn.source == RoleArnSource.SOURCE_PROFILE) { attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SOURCE_PROFILE) } diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/profile/ProfileChain.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/profile/ProfileChain.kt index 499330a229c..b43f803341e 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/profile/ProfileChain.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/profile/ProfileChain.kt @@ -112,7 +112,7 @@ internal data class RoleArn( /** * The source used to create the [RoleArn] */ - val roleArnSource: RoleArnSource? = null, + val source: RoleArnSource? = null, /** * Session name to pass to the assume role provider diff --git a/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_assume_role/test-case.json b/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_assume_role/test-case.json index 8d53f734c01..aea0e7d55c4 100644 --- a/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_assume_role/test-case.json +++ b/aws-runtime/aws-config/common/test-resources/default-provider-chain/imds_assume_role/test-case.json @@ -1,6 +1,6 @@ { "name": "imds-token-fail", - "docs": "acquires credentials via IMDS and uses them to assume a role", + "docs": "acquires credentials from a profile with IMDS as a named provider and uses them to assume a role", "result": { "Ok": { "access_key_id": "ASIARCORRECT", diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt index 39540306d82..3dc0e76ed9e 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt @@ -328,7 +328,7 @@ class ProfileCredentialsProviderTest { } @Test - fun staticCredentialsBusinessMetrics() = runTest { + fun profileCredentialsBusinessMetrics() = runTest { val testProvider = TestPlatformProvider( env = mapOf("AWS_CONFIG_FILE" to "config"), fs = mapOf( From 1a51409266433e465cc49d4edf80f21ecc18c0a5 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 17 Oct 2024 13:51:07 -0400 Subject: [PATCH 09/20] Fix CODE_CREDENTIALS metric to only emit when static credntials provider is configured by user --- .../sdk/kotlin/codegen/BusinessMetricsIntegration.kt | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt index e5168d2de08..75b07147fa9 100644 --- a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt +++ b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt @@ -71,16 +71,9 @@ class BusinessMetricsIntegration : KotlinIntegration { override val name: String = "credentialsOverrideBusinessMetricsMiddleware" override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) { writer.withBlock( - "if (config.credentialsProvider.#T != #S ) {", + "if (config.credentialsProvider is #T) {", "}", - RuntimeTypes.Auth.Credentials.AwsCredentials.simpleClassName, - /** - * If a [CredentialsProvider] is not wrapped by [aws.sdk.kotlin.runtime.auth.credentials.internal.ManagedCredentialsProvider] - * it means that it was not created by the SDK, suggesting that the user has supplied their own [CredentialsProvider]. - * - * See:[AwsServiceConfigIntegration.CredentialsProviderProp] - */ - "ManagedCredentialsProvider", + AwsRuntimeTypes.Config.Credentials.StaticCredentialsProvider, ) { write( "op.context.#T(#T.Credentials.CREDENTIALS_CODE)", From d7ba2b9ac3ce0dda8b2ff17faccdd002eaddab7e Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 17 Oct 2024 15:10:49 -0400 Subject: [PATCH 10/20] Refactor CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN business metrics out of StsWebIdentityCredentialsProvider --- aws-runtime/aws-config/api/aws-config.api | 5 +- .../DefaultChainCredentialsProvider.kt | 11 +++- .../LazilyInitializedCredentialsProvider.kt | 15 ++++- .../StsWebIdentityCredentialsProvider.kt | 58 +------------------ 4 files changed, 26 insertions(+), 63 deletions(-) diff --git a/aws-runtime/aws-config/api/aws-config.api b/aws-runtime/aws-config/api/aws-config.api index db7971c9168..46d56447510 100644 --- a/aws-runtime/aws-config/api/aws-config.api +++ b/aws-runtime/aws-config/api/aws-config.api @@ -98,8 +98,8 @@ public final class aws/sdk/kotlin/runtime/auth/credentials/InvalidSsoTokenExcept } public final class aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider : aws/smithy/kotlin/runtime/auth/awscredentials/CredentialsProvider { - public fun (Ljava/lang/String;Lkotlin/jvm/functions/Function0;)V - public synthetic fun (Ljava/lang/String;Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Laws/smithy/kotlin/runtime/operation/ExecutionContext;Lkotlin/jvm/functions/Function0;)V + public synthetic fun (Ljava/lang/String;Laws/smithy/kotlin/runtime/operation/ExecutionContext;Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun resolve (Laws/smithy/kotlin/runtime/collections/Attributes;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun toString ()Ljava/lang/String; } @@ -203,7 +203,6 @@ public final class aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredent public synthetic fun (Laws/sdk/kotlin/runtime/auth/credentials/AssumeRoleWithWebIdentityParameters;Ljava/lang/String;Laws/smithy/kotlin/runtime/util/PlatformProvider;Laws/smithy/kotlin/runtime/http/engine/HttpClientEngine;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JLaws/smithy/kotlin/runtime/util/PlatformProvider;Laws/smithy/kotlin/runtime/http/engine/HttpClientEngine;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JLaws/smithy/kotlin/runtime/util/PlatformProvider;Laws/smithy/kotlin/runtime/http/engine/HttpClientEngine;Lkotlin/jvm/internal/DefaultConstructorMarker;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JLaws/smithy/kotlin/runtime/util/PlatformProvider;Laws/smithy/kotlin/runtime/http/engine/HttpClientEngine;ZLkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getHttpClient ()Laws/smithy/kotlin/runtime/http/engine/HttpClientEngine; public final fun getPlatformProvider ()Laws/smithy/kotlin/runtime/util/PlatformProvider; public final fun getRegion ()Ljava/lang/String; diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt index 2aed4c42198..ad42dad5eed 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt @@ -7,13 +7,15 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.config.imds.ImdsClient +import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.* +import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics import aws.smithy.kotlin.runtime.collections.Attributes -import aws.smithy.kotlin.runtime.collections.get import aws.smithy.kotlin.runtime.http.engine.DefaultHttpEngine import aws.smithy.kotlin.runtime.http.engine.HttpClientEngine import aws.smithy.kotlin.runtime.io.Closeable import aws.smithy.kotlin.runtime.io.closeIfCloseable +import aws.smithy.kotlin.runtime.operation.ExecutionContext import aws.smithy.kotlin.runtime.util.PlatformProvider /** @@ -53,7 +55,12 @@ public class DefaultChainCredentialsProvider constructor( private val chain = CredentialsProviderChain( SystemPropertyCredentialsProvider(platformProvider::getProperty), EnvironmentCredentialsProvider(platformProvider::getenv), - LazilyInitializedCredentialsProvider("EnvironmentStsWebIdentityCredentialsProvider") { + LazilyInitializedCredentialsProvider( + "EnvironmentStsWebIdentityCredentialsProvider", + ExecutionContext.build { + attributes[BusinessMetrics] = mutableSetOf(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN.identifier) + }, + ) { // STS web identity provider can be constructed from either the profile OR 100% from the environment StsWebIdentityCredentialsProvider.fromEnvironment( platformProvider = platformProvider, diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt index 0d3315a07ea..e6f1e64eb30 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt @@ -2,7 +2,12 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider +import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics +import aws.smithy.kotlin.runtime.businessmetrics.mergeBusinessMetrics import aws.smithy.kotlin.runtime.collections.Attributes +import aws.smithy.kotlin.runtime.collections.MutableAttributes +import aws.smithy.kotlin.runtime.collections.mergeExcept +import aws.smithy.kotlin.runtime.operation.ExecutionContext /** * A [CredentialsProvider] implementation that delays the initialization of the underlying provider until @@ -10,15 +15,23 @@ import aws.smithy.kotlin.runtime.collections.Attributes * or should be deferred until credentials are actually needed. * * @param providerName The name of the credentials provider that is being wrapped. Will default to "LazilyInitializedCredentialsProvider". + * @param executionContext Additional execution context to use when resolving credentials. Will default to an empty execution context. * @param initializer A lambda function that provides the actual [CredentialsProvider] to be initialized lazily. */ public class LazilyInitializedCredentialsProvider( private val providerName: String = "LazilyInitializedCredentialsProvider", + private val executionContext: ExecutionContext = ExecutionContext(), initializer: () -> CredentialsProvider, ) : CredentialsProvider { private val provider = lazy(initializer) - override suspend fun resolve(attributes: Attributes): Credentials = provider.value.resolve(attributes) + override suspend fun resolve(attributes: Attributes): Credentials { + if (attributes is MutableAttributes) { + attributes.mergeExcept(executionContext, exceptions = setOf(BusinessMetrics)) + attributes.mergeBusinessMetrics(executionContext) + } + return provider.value.resolve(attributes) + } override fun toString(): String = providerName } diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt index 0959d885f94..19f6f0ab40f 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt @@ -44,10 +44,6 @@ public class StsWebIdentityCredentialsProvider( public val platformProvider: PlatformProvider = PlatformProvider.System, public val httpClient: HttpClientEngine? = null, ) : CredentialsProvider { - /** - * Indicates if the class was created using [fromEnvironment] - */ - private var createdFromEnvironment: Boolean = false /** * A [CredentialsProvider] that exchanges a Web Identity Token for credentials from the AWS Security Token Service @@ -85,46 +81,6 @@ public class StsWebIdentityCredentialsProvider( httpClient, ) - /** - * A [CredentialsProvider] that exchanges a Web Identity Token for credentials from the AWS Security Token Service - * (STS). - * - * @param roleArn The ARN of the target role to assume, e.g. `arn:aws:iam:123456789:role/example` - * @param webIdentityTokenFilePath The path to the file containing a JWT token - * @param region The AWS region to assume the role in - * @param roleSessionName The name to associate with the session. Use the role session name to uniquely identify a - * session when the same role is assumed by different principals or for different reasons. In cross-account - * scenarios, the role session name is visible to, and can be logged by the account that owns the role. The role - * session name is also in the ARN of the assumed role principal. - * @param duration The expiry duration of the credentials. Defaults to 15 minutes if not set. - * @param platformProvider The platform API provider - * @param httpClient the [HttpClientEngine] instance to use to make requests. NOTE: This engine's resources and - * lifetime are NOT managed by the provider. Caller is responsible for closing. - * @param createdFromEnvironment If the [StsWebIdentityCredentialsProvider] was created using [fromEnvironment]. - */ - private constructor( - roleArn: String, - webIdentityTokenFilePath: String, - region: String?, - roleSessionName: String? = null, - duration: Duration = DEFAULT_CREDENTIALS_REFRESH_SECONDS.seconds, - platformProvider: PlatformProvider = PlatformProvider.System, - httpClient: HttpClientEngine? = null, - createdFromEnvironment: Boolean, - ) : this( - AssumeRoleWithWebIdentityParameters( - roleArn = roleArn, - webIdentityTokenFilePath = webIdentityTokenFilePath, - roleSessionName = roleSessionName, - duration = duration, - ), - region, - platformProvider, - httpClient, - ) { - this.createdFromEnvironment = createdFromEnvironment - } - public companion object { /** * Create an [StsWebIdentityCredentialsProvider] from the current execution environment. This will attempt @@ -143,16 +99,7 @@ public class StsWebIdentityCredentialsProvider( val resolvedRoleArn = platformProvider.resolve(roleArn, AwsSdkSetting.AwsRoleArn, "roleArn") val resolvedTokenFilePath = platformProvider.resolve(webIdentityTokenFilePath, AwsSdkSetting.AwsWebIdentityTokenFile, "webIdentityTokenFilePath") val resolvedRegion = region ?: AwsSdkSetting.AwsRegion.resolve(platformProvider) - return StsWebIdentityCredentialsProvider( - resolvedRoleArn, - resolvedTokenFilePath, - resolvedRegion, - roleSessionName, - duration, - platformProvider, - httpClient, - createdFromEnvironment = true, - ) + return StsWebIdentityCredentialsProvider(resolvedRoleArn, resolvedTokenFilePath, resolvedRegion, roleSessionName, duration, platformProvider, httpClient) } } @@ -160,9 +107,6 @@ public class StsWebIdentityCredentialsProvider( val logger = coroutineContext.logger() logger.debug { "retrieving assumed credentials via web identity" } - if (createdFromEnvironment) { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN) - } attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE_WEB_ID) val provider = this From 668ed186b4db779e15a00ec7157c1d251c6e1a81 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 24 Oct 2024 10:57:10 -0400 Subject: [PATCH 11/20] Move credential business metric emission to credentials return time --- aws-runtime/aws-config/api/aws-config.api | 4 +- .../DefaultChainCredentialsProvider.kt | 6 +- .../credentials/EcsCredentialsProvider.kt | 6 +- .../EnvironmentCredentialsProvider.kt | 6 +- .../credentials/ImdsCredentialsProvider.kt | 3 +- .../LazilyInitializedCredentialsProvider.kt | 19 +-- .../credentials/ProcessCredentialsProvider.kt | 22 ++-- .../credentials/ProfileCredentialsProvider.kt | 21 +-- .../credentials/SsoCredentialsProvider.kt | 10 +- .../StsAssumeRoleCredentialsProvider.kt | 6 +- .../StsWebIdentityCredentialsProvider.kt | 6 +- .../SystemPropertyCredentialsProvider.kt | 6 +- .../ProfileCredentialsProviderTest.kt | 122 ------------------ 13 files changed, 57 insertions(+), 180 deletions(-) diff --git a/aws-runtime/aws-config/api/aws-config.api b/aws-runtime/aws-config/api/aws-config.api index 46d56447510..84d6427c300 100644 --- a/aws-runtime/aws-config/api/aws-config.api +++ b/aws-runtime/aws-config/api/aws-config.api @@ -98,8 +98,8 @@ public final class aws/sdk/kotlin/runtime/auth/credentials/InvalidSsoTokenExcept } public final class aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider : aws/smithy/kotlin/runtime/auth/awscredentials/CredentialsProvider { - public fun (Ljava/lang/String;Laws/smithy/kotlin/runtime/operation/ExecutionContext;Lkotlin/jvm/functions/Function0;)V - public synthetic fun (Ljava/lang/String;Laws/smithy/kotlin/runtime/operation/ExecutionContext;Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetric;Lkotlin/jvm/functions/Function0;)V + public synthetic fun (Ljava/lang/String;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetric;Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun resolve (Laws/smithy/kotlin/runtime/collections/Attributes;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun toString ()Ljava/lang/String; } diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt index ad42dad5eed..5d7cac27b25 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt @@ -9,13 +9,11 @@ import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.config.imds.ImdsClient import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.* -import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.http.engine.DefaultHttpEngine import aws.smithy.kotlin.runtime.http.engine.HttpClientEngine import aws.smithy.kotlin.runtime.io.Closeable import aws.smithy.kotlin.runtime.io.closeIfCloseable -import aws.smithy.kotlin.runtime.operation.ExecutionContext import aws.smithy.kotlin.runtime.util.PlatformProvider /** @@ -57,9 +55,7 @@ public class DefaultChainCredentialsProvider constructor( EnvironmentCredentialsProvider(platformProvider::getenv), LazilyInitializedCredentialsProvider( "EnvironmentStsWebIdentityCredentialsProvider", - ExecutionContext.build { - attributes[BusinessMetrics] = mutableSetOf(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN.identifier) - }, + AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN, ) { // STS web identity provider can be constructed from either the profile OR 100% from the environment StsWebIdentityCredentialsProvider.fromEnvironment( diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt index 6250a9db725..23c504e9a4d 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt @@ -79,8 +79,6 @@ public class EcsCredentialsProvider( override suspend fun resolve(attributes: Attributes): Credentials { val logger = coroutineContext.logger() - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_HTTP) - val authToken = loadAuthToken() val relativeUri = AwsSdkSetting.AwsContainerCredentialsRelativeUri.resolve(platformProvider) val fullUri = AwsSdkSetting.AwsContainerCredentialsFullUri.resolve(platformProvider) @@ -115,7 +113,9 @@ public class EcsCredentialsProvider( logger.debug { "obtained credentials from container metadata service; expiration=${creds.expiration?.format(TimestampFormat.ISO_8601)}" } - return creds + return creds.also { + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_HTTP) + } } private suspend fun loadAuthToken(): String? { diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProvider.kt index 9487f95a584..cfb49dca498 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProvider.kt @@ -39,15 +39,15 @@ public class EnvironmentCredentialsProvider( "Attempting to load credentials from env vars $ACCESS_KEY_ID/$SECRET_ACCESS_KEY/$SESSION_TOKEN" } - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS) - return credentials( accessKeyId = requireEnv(ACCESS_KEY_ID), secretAccessKey = requireEnv(SECRET_ACCESS_KEY), sessionToken = getEnv(SESSION_TOKEN), providerName = PROVIDER_NAME, accountId = getEnv(ACCOUNT_ID), - ) + ).also { + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS) + } } override fun toString(): String = this.simpleClassName diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt index 824432cb712..d2a37a164aa 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt @@ -62,8 +62,6 @@ public class ImdsCredentialsProvider( private val mu = Mutex() override suspend fun resolve(attributes: Attributes): Credentials { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) - if (AwsSdkSetting.AwsEc2MetadataDisabled.resolve(platformProvider) == true) { throw CredentialsNotLoadedException("AWS EC2 metadata is explicitly disabled; credentials not loaded") } @@ -114,6 +112,7 @@ public class ImdsCredentialsProvider( creds.also { mu.withLock { previousCredentials = it } + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) } } is JsonCredentialsResponse.Error -> { diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt index e6f1e64eb30..df48fb82958 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt @@ -2,12 +2,9 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider -import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics -import aws.smithy.kotlin.runtime.businessmetrics.mergeBusinessMetrics +import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric +import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.Attributes -import aws.smithy.kotlin.runtime.collections.MutableAttributes -import aws.smithy.kotlin.runtime.collections.mergeExcept -import aws.smithy.kotlin.runtime.operation.ExecutionContext /** * A [CredentialsProvider] implementation that delays the initialization of the underlying provider until @@ -15,22 +12,18 @@ import aws.smithy.kotlin.runtime.operation.ExecutionContext * or should be deferred until credentials are actually needed. * * @param providerName The name of the credentials provider that is being wrapped. Will default to "LazilyInitializedCredentialsProvider". - * @param executionContext Additional execution context to use when resolving credentials. Will default to an empty execution context. + * @param businessMetric The provider's business metric to emit when credentials are resolved. * @param initializer A lambda function that provides the actual [CredentialsProvider] to be initialized lazily. */ public class LazilyInitializedCredentialsProvider( private val providerName: String = "LazilyInitializedCredentialsProvider", - private val executionContext: ExecutionContext = ExecutionContext(), + private val businessMetric: BusinessMetric, initializer: () -> CredentialsProvider, ) : CredentialsProvider { private val provider = lazy(initializer) - override suspend fun resolve(attributes: Attributes): Credentials { - if (attributes is MutableAttributes) { - attributes.mergeExcept(executionContext, exceptions = setOf(BusinessMetrics)) - attributes.mergeBusinessMetrics(executionContext) - } - return provider.value.resolve(attributes) + override suspend fun resolve(attributes: Attributes): Credentials = provider.value.resolve(attributes).also { + attributes.emitBusinessMetric(businessMetric) } override fun toString(): String = providerName diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider.kt index 1d9b4ad39eb..47c331db55b 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider.kt @@ -55,8 +55,6 @@ public class ProcessCredentialsProvider( override suspend fun resolve(attributes: Attributes): Credentials { val logger = coroutineContext.logger() - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS) - val (exitCode, output) = try { executeCommand(credentialProcess, platformProvider, maxOutputLengthBytes, timeoutMillis) } catch (ex: Exception) { @@ -72,14 +70,18 @@ public class ProcessCredentialsProvider( val deserializer = JsonDeserializer(payload) return when (val resp = deserializeJsonProcessCredentials(deserializer)) { - is JsonCredentialsResponse.SessionCredentials -> credentials( - resp.accessKeyId, - resp.secretAccessKey, - resp.sessionToken, - resp.expiration ?: Instant.MAX_VALUE, - PROVIDER_NAME, - resp.accountId, - ) + is JsonCredentialsResponse.SessionCredentials -> { + credentials( + resp.accessKeyId, + resp.secretAccessKey, + resp.sessionToken, + resp.expiration ?: Instant.MAX_VALUE, + PROVIDER_NAME, + resp.accountId, + ).also { + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS) + } + } else -> throw CredentialsProviderException("Credentials response was not of expected format") } } diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt index 6d7aa1e4fe8..a255092b18f 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt @@ -89,6 +89,7 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( public val httpClient: HttpClientEngine? = null, public val configurationSource: AwsConfigurationSource? = null, ) : CloseableCredentialsProvider { + private val credentialsBusinessMetrics: MutableSet = mutableSetOf() public constructor( profileName: String? = null, @@ -135,7 +136,7 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( chain.roles.forEach { roleArn -> logger.debug { "Assuming role `${roleArn.roleArn}`" } if (roleArn.source == RoleArnSource.SOURCE_PROFILE) { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SOURCE_PROFILE) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SOURCE_PROFILE) } val assumeProvider = roleArn.toCredentialsProvider(creds, region) @@ -143,7 +144,11 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( } logger.debug { "Obtained credentials from profile; expiration=${creds.expiration?.format(TimestampFormat.ISO_8601)}" } - return creds + return creds.also { + credentialsBusinessMetrics.forEach { metric -> + attributes.emitBusinessMetric(metric) + } + } } override fun close() { @@ -155,18 +160,18 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( private suspend fun LeafProvider.toCredentialsProvider(region: LazyAsyncValue, attributes: Attributes): CredentialsProvider = when (this) { is LeafProvider.NamedSource -> { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_NAMED_PROVIDER) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_NAMED_PROVIDER) namedProviders[name] ?: throw ProviderConfigurationException("unknown credentials source: $name") } is LeafProvider.AccessKey -> { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) StaticCredentialsProvider(credentials) } is LeafProvider.WebIdentityTokenRole -> { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN) StsWebIdentityCredentialsProvider( roleArn, webIdentityTokenFile, @@ -178,7 +183,7 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( } is LeafProvider.SsoSession -> { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO) SsoCredentialsProvider( accountId = ssoAccountId, roleName = ssoRoleName, @@ -191,7 +196,7 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( } is LeafProvider.LegacySso -> { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO_LEGACY) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO_LEGACY) SsoCredentialsProvider( accountId = ssoAccountId, roleName = ssoRoleName, @@ -203,7 +208,7 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( } is LeafProvider.Process -> { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_PROCESS) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_PROCESS) ProcessCredentialsProvider(command) } } diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProvider.kt index 9427a6f9de0..e0956c22fc9 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProvider.kt @@ -94,11 +94,9 @@ public class SsoCredentialsProvider public constructor( val token = if (ssoTokenProvider != null) { logger.trace { "Attempting to load token using token provider for sso-session: `$ssoSessionName`" } - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_SSO) ssoTokenProvider.resolve(attributes) } else { logger.trace { "Attempting to load token from file using legacy format" } - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_SSO_LEGACY) legacyLoadTokenFile() } @@ -132,7 +130,13 @@ public class SsoCredentialsProvider public constructor( expiration = Instant.fromEpochMilliseconds(roleCredentials.expiration), PROVIDER_NAME, accountId = accountId, - ) + ).also { + if (ssoTokenProvider != null) { + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_SSO) + } else { + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_SSO_LEGACY) + } + } } // non sso-session legacy token flow diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProvider.kt index 2412f69e8d9..ee81a43db1b 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProvider.kt @@ -97,8 +97,6 @@ public class StsAssumeRoleCredentialsProvider( val logger = coroutineContext.logger() logger.debug { "retrieving assumed credentials" } - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE) - // NOTE: multi region access points require regional STS endpoints val provider = this val telemetry = coroutineContext.telemetryProvider @@ -150,7 +148,9 @@ public class StsAssumeRoleCredentialsProvider( expiration = roleCredentials.expiration, providerName = PROVIDER_NAME, accountId = accountId, - ) + ).also { + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE) + } } override fun toString(): String = this.simpleClassName diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt index 19f6f0ab40f..adf05453b68 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt @@ -107,8 +107,6 @@ public class StsWebIdentityCredentialsProvider( val logger = coroutineContext.logger() logger.debug { "retrieving assumed credentials via web identity" } - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE_WEB_ID) - val provider = this val params = provider.webIdentityParameters @@ -154,7 +152,9 @@ public class StsWebIdentityCredentialsProvider( expiration = roleCredentials.expiration, providerName = PROVIDER_NAME, accountId = accountId, - ) + ).also { + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE_WEB_ID) + } } override fun toString(): String = this.simpleClassName diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProvider.kt index c5d84e42f4b..21aebc7c11f 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProvider.kt @@ -39,15 +39,15 @@ public class SystemPropertyCredentialsProvider( "Attempting to load credentials from system properties $ACCESS_KEY_ID/$SECRET_ACCESS_KEY/$SESSION_TOKEN" } - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_JVM_SYSTEM_PROPERTIES) - return credentials( accessKeyId = requireProperty(ACCESS_KEY_ID), secretAccessKey = requireProperty(SECRET_ACCESS_KEY), sessionToken = getProperty(SESSION_TOKEN), providerName = PROVIDER_NAME, accountId = getProperty(ACCOUNT_ID), - ) + ).also { + attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_JVM_SYSTEM_PROPERTIES) + } } override fun toString(): String = this.simpleClassName diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt index 3dc0e76ed9e..22fefabb8c1 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt @@ -9,7 +9,6 @@ import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.client.AwsClientOption import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials -import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics import aws.smithy.kotlin.runtime.collections.attributesOf import aws.smithy.kotlin.runtime.collections.get @@ -440,127 +439,6 @@ class ProfileCredentialsProviderTest { assertEquals(expected, actual) } - @Test - fun assumeRoleWithWebIdentityTokenBusinessMetrics() = runTest { - val testArn = "arn:aws:iam::1234567:role/test-role" - val testProvider = TestPlatformProvider( - env = mapOf( - "AWS_CONFIG_FILE" to "config", - "AWS_REGION" to "us-west-2", - ), - fs = mapOf( - "config" to """ - [default] - role_arn = $testArn - web_identity_token_file = /some/path/to/test-token - """.trimIndent(), - "/some/path/to/test-token" to "token", - ), - ) - val testEngine = buildTestConnection { - expect(StsTestUtils.stsRequest(emptyMap()), StsTestUtils.stsResponse(testArn)) - } - - val provider = ProfileCredentialsProvider( - platformProvider = testProvider, - httpClient = testEngine, - ) - val attributes = ExecutionContext() - try { - provider.resolve(attributes) - } catch (e: CredentialsProviderException) { - if (e.message != "STS failed to assume role from web identity") throw e - } - - assertTrue(attributes.contains(BusinessMetrics)) - - val actual = attributes[BusinessMetrics] - val expected = setOf( - AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN.identifier, - AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE_WEB_ID.identifier, - ) - assertEquals(expected, actual) - } - - @Test - fun ssoBusinessMetrics() = runTest { - val testProvider = TestPlatformProvider( - env = mapOf( - "AWS_CONFIG_FILE" to "config", - ), - fs = mapOf( - "config" to """ - [default] - sso_session = B - sso_account_id = 012345678901 - sso_role_name = SampleRole - - [sso-session B] - sso_region = us-east-1 - sso_start_url = https://d-abc123.awsapps.com/start - """.trimIndent(), - ), - ) - val testEngine = TestConnection() - val provider = ProfileCredentialsProvider( - platformProvider = testProvider, - httpClient = testEngine, - ) - val attributes = ExecutionContext() - try { - provider.resolve(attributes) - } catch (e: ProviderConfigurationException) { - if (e.message != "Invalid or missing SSO session cache. Run `aws sso login` to initiate a new SSO session") throw e - } - - assertTrue(attributes.contains(BusinessMetrics)) - - val actual = attributes[BusinessMetrics] - val expected = setOf( - AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO.identifier, - AwsBusinessMetric.Credentials.CREDENTIALS_SSO.identifier, - ) - assertEquals(expected, actual) - } - - @Test - fun legacySsoBusinessMetrics() = runTest { - val testProvider = TestPlatformProvider( - env = mapOf( - "AWS_CONFIG_FILE" to "config", - ), - fs = mapOf( - "config" to """ - [default] - sso_account_id = 012345678901 - sso_region = us-east-1 - sso_role_name = SampleRole - sso_start_url = https://d-abc123.awsapps.com/start-beta - """.trimIndent(), - ), - ) - val testEngine = TestConnection() - val provider = ProfileCredentialsProvider( - platformProvider = testProvider, - httpClient = testEngine, - ) - val attributes = ExecutionContext() - try { - provider.resolve(attributes) - } catch (e: ProviderConfigurationException) { - if (e.message != "Invalid or missing SSO session cache. Run `aws sso login` to initiate a new SSO session") throw e - } - - assertTrue(attributes.contains(BusinessMetrics)) - - val actual = attributes[BusinessMetrics] - val expected = setOf( - AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO_LEGACY.identifier, - AwsBusinessMetric.Credentials.CREDENTIALS_SSO_LEGACY.identifier, - ) - assertEquals(expected, actual) - } - @Test fun processBusinessMetrics() = runTest { val testProvider = TestPlatformProvider( From 1bdaa80154486743ec6d1d3eb9f64e5eeea22318 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Mon, 28 Oct 2024 15:46:13 -0400 Subject: [PATCH 12/20] Remove duplicate test coverage & newline in LazilyInitializedCredentialsProvider --- .../LazilyInitializedCredentialsProvider.kt | 7 +- .../ProfileCredentialsProviderTest.kt | 73 ------------------- 2 files changed, 4 insertions(+), 76 deletions(-) diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt index df48fb82958..3bcdb8e9660 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt @@ -22,9 +22,10 @@ public class LazilyInitializedCredentialsProvider( ) : CredentialsProvider { private val provider = lazy(initializer) - override suspend fun resolve(attributes: Attributes): Credentials = provider.value.resolve(attributes).also { - attributes.emitBusinessMetric(businessMetric) - } + override suspend fun resolve(attributes: Attributes): Credentials = + provider.value.resolve(attributes).also { + attributes.emitBusinessMetric(businessMetric) + } override fun toString(): String = providerName } diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt index 22fefabb8c1..b06f62e7738 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt @@ -326,79 +326,6 @@ class ProfileCredentialsProviderTest { assertEquals(expected, actual) } - @Test - fun profileCredentialsBusinessMetrics() = runTest { - val testProvider = TestPlatformProvider( - env = mapOf("AWS_CONFIG_FILE" to "config"), - fs = mapOf( - "config" to """ - [default] - aws_access_key_id = AKID-Default - aws_secret_access_key = Default-Secret - """.trimIndent(), - ), - ) - val testEngine = TestConnection() - - val provider = ProfileCredentialsProvider( - platformProvider = testProvider, - httpClient = testEngine, - ) - val attributes = ExecutionContext() - provider.resolve(attributes) - - assertTrue(attributes.contains(BusinessMetrics)) - - val actual = attributes[BusinessMetrics] - val expected = setOf( - AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE.identifier, - ) - assertEquals(expected, actual) - } - - @Test - fun assumeRoleWithSourceProfileBusinessMetrics() = runTest { - val testArn = "arn:aws:iam::1234567:role/test-role" - val testProvider = TestPlatformProvider( - env = mapOf( - "AWS_CONFIG_FILE" to "config", - "AWS_REGION" to "us-west-2", - ), - fs = mapOf( - "config" to """ - [default] - role_arn = $testArn - source_profile = B - - [profile B] - region = us-east-1 - aws_access_key_id = AKID-Profile - aws_secret_access_key = Profile-Secret - """.trimIndent(), - ), - ) - val testEngine = buildTestConnection { - expect(StsTestUtils.stsResponse(testArn)) - } - - val provider = ProfileCredentialsProvider( - platformProvider = testProvider, - httpClient = testEngine, - ) - val attributes = ExecutionContext() - provider.resolve(attributes) - - assertTrue(attributes.contains(BusinessMetrics)) - - val actual = attributes[BusinessMetrics] - val expected = setOf( - AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SOURCE_PROFILE.identifier, - AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE.identifier, - AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE.identifier, - ) - assertEquals(expected, actual) - } - @Test fun assumeRoleWithNamedProviderBusinessMetrics() = runTest { val testArn = "arn:aws:iam::1234567:role/test-role" From 03ca91f70630658322fdd1dbe8960dafc95782b5 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Mon, 28 Oct 2024 16:17:51 -0400 Subject: [PATCH 13/20] Refactor ProfileCredentialsProvider, unused function param, and readability changes --- .../credentials/ProfileCredentialsProvider.kt | 69 +++++++++---------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt index a255092b18f..d0b4bb8fe55 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt @@ -129,7 +129,7 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( val profileOverride = profileName?.let { sharedConfig.profiles[it] } val region = asyncLazy { region ?: profileOverride?.getOrNull("region") ?: attributes.getOrNull(AwsClientOption.Region) ?: resolveRegion(platformProvider) } - val leaf = chain.leaf.toCredentialsProvider(region, attributes) + val leaf = chain.leaf.toCredentialsProvider(region) logger.debug { "Resolving credentials from ${chain.leaf.description()}" } var creds = leaf.resolve(attributes) @@ -157,59 +157,52 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( } } - private suspend fun LeafProvider.toCredentialsProvider(region: LazyAsyncValue, attributes: Attributes): CredentialsProvider = + private suspend fun LeafProvider.toCredentialsProvider(region: LazyAsyncValue): CredentialsProvider = when (this) { - is LeafProvider.NamedSource -> { + is LeafProvider.NamedSource -> namedProviders[name].also { credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_NAMED_PROVIDER) - namedProviders[name] - ?: throw ProviderConfigurationException("unknown credentials source: $name") - } + } ?: throw ProviderConfigurationException("unknown credentials source: $name") - is LeafProvider.AccessKey -> { + is LeafProvider.AccessKey -> StaticCredentialsProvider(credentials).also { credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) - StaticCredentialsProvider(credentials) } - is LeafProvider.WebIdentityTokenRole -> { + is LeafProvider.WebIdentityTokenRole -> StsWebIdentityCredentialsProvider( + roleArn, + webIdentityTokenFile, + region = region.get(), + roleSessionName = sessionName, + platformProvider = platformProvider, + httpClient = httpClient, + ).also { credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN) - StsWebIdentityCredentialsProvider( - roleArn, - webIdentityTokenFile, - region = region.get(), - roleSessionName = sessionName, - platformProvider = platformProvider, - httpClient = httpClient, - ) } - is LeafProvider.SsoSession -> { + is LeafProvider.SsoSession -> SsoCredentialsProvider( + accountId = ssoAccountId, + roleName = ssoRoleName, + startUrl = ssoStartUrl, + ssoRegion = ssoRegion, + ssoSessionName = ssoSessionName, + httpClient = httpClient, + platformProvider = platformProvider, + ).also { credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO) - SsoCredentialsProvider( - accountId = ssoAccountId, - roleName = ssoRoleName, - startUrl = ssoStartUrl, - ssoRegion = ssoRegion, - ssoSessionName = ssoSessionName, - httpClient = httpClient, - platformProvider = platformProvider, - ) } - is LeafProvider.LegacySso -> { + is LeafProvider.LegacySso -> SsoCredentialsProvider( + accountId = ssoAccountId, + roleName = ssoRoleName, + startUrl = ssoStartUrl, + ssoRegion = ssoRegion, + httpClient = httpClient, + platformProvider = platformProvider, + ).also { credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO_LEGACY) - SsoCredentialsProvider( - accountId = ssoAccountId, - roleName = ssoRoleName, - startUrl = ssoStartUrl, - ssoRegion = ssoRegion, - httpClient = httpClient, - platformProvider = platformProvider, - ) } - is LeafProvider.Process -> { + is LeafProvider.Process -> ProcessCredentialsProvider(command).also { credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_PROCESS) - ProcessCredentialsProvider(command) } } From 6a7b8121229afbc5ba0e1e3d19812750540864e5 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 31 Oct 2024 16:46:55 -0400 Subject: [PATCH 14/20] Move credentials business metrics to credentials attributes --- .../DefaultChainCredentialsProvider.kt | 2 +- .../credentials/EcsCredentialsProvider.kt | 8 +- .../EnvironmentCredentialsProvider.kt | 8 +- .../credentials/ImdsCredentialsProvider.kt | 7 +- .../LazilyInitializedCredentialsProvider.kt | 13 +- .../credentials/ProcessCredentialsProvider.kt | 13 +- .../credentials/ProfileCredentialsProvider.kt | 37 +++--- .../credentials/SsoCredentialsProvider.kt | 24 ++-- .../StsAssumeRoleCredentialsProvider.kt | 8 +- .../StsWebIdentityCredentialsProvider.kt | 8 +- .../SystemPropertyCredentialsProvider.kt | 8 +- .../credentials/EcsCredentialsProviderTest.kt | 6 +- .../EnvironmentCredentialsProviderTest.kt | 24 +++- .../ImdsCredentialsProviderTest.kt | 14 ++- .../ProfileCredentialsProviderTest.kt | 68 ++++++---- .../credentials/SsoCredentialsProviderTest.kt | 11 +- .../StsAssumeRoleCredentialsProviderTest.kt | 35 +++++- .../StsWebIdentityCredentialsProviderTest.kt | 4 +- .../SystemPropertyCredentialsProviderTest.kt | 28 ++++- .../util/AwsBusinessMetricsTestUtils.kt | 33 +++++ .../DefaultChainCredentialsProviderTest.kt | 16 +-- .../ProcessCredentialsProviderTest.kt | 8 +- aws-runtime/aws-http/api/aws-http.api | 77 ++++++------ .../BusinessMetricsInterceptor.kt | 89 ------------- .../AwsBusinessMetricsUtils.kt | 117 ++++++++++++++++++ .../BusinessMetricsInterceptor.kt | 30 +++++ .../BusinessMetricsInterceptorTest.kt | 2 + .../aws/sdk/kotlin/codegen/AwsRuntimeTypes.kt | 7 +- .../codegen/BusinessMetricsIntegration.kt | 8 +- 29 files changed, 445 insertions(+), 268 deletions(-) create mode 100644 aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt delete mode 100644 aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptor.kt create mode 100644 aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt create mode 100644 aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/BusinessMetricsInterceptor.kt diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt index 5d7cac27b25..d1d3e41ecf6 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt @@ -7,7 +7,7 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.config.imds.ImdsClient -import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.* import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.http.engine.DefaultHttpEngine diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt index 23c504e9a4d..655d0a6eb06 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt @@ -7,10 +7,10 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting -import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.ErrorMetadata import aws.smithy.kotlin.runtime.auth.awscredentials.* -import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.client.endpoints.Endpoint import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.config.resolve @@ -113,9 +113,7 @@ public class EcsCredentialsProvider( logger.debug { "obtained credentials from container metadata service; expiration=${creds.expiration?.format(TimestampFormat.ISO_8601)}" } - return creds.also { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_HTTP) - } + return creds.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_HTTP) } private suspend fun loadAuthToken(): String? { diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProvider.kt index cfb49dca498..f24122b1355 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProvider.kt @@ -7,11 +7,11 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting -import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider import aws.smithy.kotlin.runtime.auth.awscredentials.simpleClassName -import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.telemetry.logging.trace import aws.smithy.kotlin.runtime.util.PlatformProvider @@ -45,9 +45,7 @@ public class EnvironmentCredentialsProvider( sessionToken = getEnv(SESSION_TOKEN), providerName = PROVIDER_NAME, accountId = getEnv(ACCOUNT_ID), - ).also { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS) - } + ).emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS) } override fun toString(): String = this.simpleClassName diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt index d2a37a164aa..928bd94f728 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt @@ -9,9 +9,9 @@ import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.config.imds.EC2MetadataError import aws.sdk.kotlin.runtime.config.imds.ImdsClient import aws.sdk.kotlin.runtime.config.imds.InstanceMetadataProvider -import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.* -import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.config.resolve import aws.smithy.kotlin.runtime.http.HttpStatusCode @@ -108,11 +108,10 @@ public class ImdsCredentialsProvider( resp.sessionToken, resp.expiration, PROVIDER_NAME, - ) + ).emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) creds.also { mu.withLock { previousCredentials = it } - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) } } is JsonCredentialsResponse.Error -> { diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt index 3bcdb8e9660..675013dea77 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt @@ -1,9 +1,9 @@ package aws.sdk.kotlin.runtime.auth.credentials +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric -import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.Attributes /** @@ -17,15 +17,16 @@ import aws.smithy.kotlin.runtime.collections.Attributes */ public class LazilyInitializedCredentialsProvider( private val providerName: String = "LazilyInitializedCredentialsProvider", - private val businessMetric: BusinessMetric, + private val businessMetric: BusinessMetric? = null, initializer: () -> CredentialsProvider, ) : CredentialsProvider { private val provider = lazy(initializer) - override suspend fun resolve(attributes: Attributes): Credentials = - provider.value.resolve(attributes).also { - attributes.emitBusinessMetric(businessMetric) - } + override suspend fun resolve(attributes: Attributes): Credentials { + val credentials = provider.value.resolve(attributes) + if (businessMetric == null) return credentials + return credentials.emitBusinessMetric(businessMetric) + } override fun toString(): String = providerName } diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider.kt index 47c331db55b..c3943c5640b 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider.kt @@ -5,12 +5,9 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials -import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric -import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials -import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider -import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException -import aws.smithy.kotlin.runtime.auth.awscredentials.simpleClassName -import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric +import aws.smithy.kotlin.runtime.auth.awscredentials.* import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.serde.json.JsonDeserializer import aws.smithy.kotlin.runtime.telemetry.logging.logger @@ -78,9 +75,7 @@ public class ProcessCredentialsProvider( resp.expiration ?: Instant.MAX_VALUE, PROVIDER_NAME, resp.accountId, - ).also { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS) - } + ).emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS) } else -> throw CredentialsProviderException("Credentials response was not of expected format") } diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt index d0b4bb8fe55..fba61523c2e 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt @@ -15,13 +15,11 @@ import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.config.imds.ImdsClient import aws.sdk.kotlin.runtime.config.profile.AwsConfigurationSource import aws.sdk.kotlin.runtime.config.profile.loadAwsSharedConfig -import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetrics import aws.sdk.kotlin.runtime.region.resolveRegion -import aws.smithy.kotlin.runtime.auth.awscredentials.CloseableCredentialsProvider -import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials -import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider -import aws.smithy.kotlin.runtime.auth.awscredentials.simpleClassName -import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric +import aws.smithy.kotlin.runtime.auth.awscredentials.* +import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.http.engine.HttpClientEngine import aws.smithy.kotlin.runtime.io.closeIfCloseable @@ -89,7 +87,7 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( public val httpClient: HttpClientEngine? = null, public val configurationSource: AwsConfigurationSource? = null, ) : CloseableCredentialsProvider { - private val credentialsBusinessMetrics: MutableSet = mutableSetOf() + private val credentialsBusinessMetrics: MutableSet = mutableSetOf() public constructor( profileName: String? = null, @@ -136,19 +134,20 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( chain.roles.forEach { roleArn -> logger.debug { "Assuming role `${roleArn.roleArn}`" } if (roleArn.source == RoleArnSource.SOURCE_PROFILE) { - credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SOURCE_PROFILE) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SOURCE_PROFILE.identifier) } val assumeProvider = roleArn.toCredentialsProvider(creds, region) + + creds.attributes.getOrNull(BusinessMetrics)?.forEach { metric -> + credentialsBusinessMetrics.add(metric) + } + creds = assumeProvider.resolve(attributes) } logger.debug { "Obtained credentials from profile; expiration=${creds.expiration?.format(TimestampFormat.ISO_8601)}" } - return creds.also { - credentialsBusinessMetrics.forEach { metric -> - attributes.emitBusinessMetric(metric) - } - } + return creds.emitBusinessMetrics(credentialsBusinessMetrics) } override fun close() { @@ -160,11 +159,11 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( private suspend fun LeafProvider.toCredentialsProvider(region: LazyAsyncValue): CredentialsProvider = when (this) { is LeafProvider.NamedSource -> namedProviders[name].also { - credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_NAMED_PROVIDER) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_NAMED_PROVIDER.identifier) } ?: throw ProviderConfigurationException("unknown credentials source: $name") is LeafProvider.AccessKey -> StaticCredentialsProvider(credentials).also { - credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE.identifier) } is LeafProvider.WebIdentityTokenRole -> StsWebIdentityCredentialsProvider( @@ -175,7 +174,7 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( platformProvider = platformProvider, httpClient = httpClient, ).also { - credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN.identifier) } is LeafProvider.SsoSession -> SsoCredentialsProvider( @@ -187,7 +186,7 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( httpClient = httpClient, platformProvider = platformProvider, ).also { - credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO.identifier) } is LeafProvider.LegacySso -> SsoCredentialsProvider( @@ -198,11 +197,11 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( httpClient = httpClient, platformProvider = platformProvider, ).also { - credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO_LEGACY) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO_LEGACY.identifier) } is LeafProvider.Process -> ProcessCredentialsProvider(command).also { - credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_PROCESS) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_PROCESS.identifier) } } diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProvider.kt index e0956c22fc9..43baacd2432 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProvider.kt @@ -8,16 +8,12 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.sso.SsoClient import aws.sdk.kotlin.runtime.auth.credentials.internal.sso.getRoleCredentials -import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric -import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials -import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider -import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException -import aws.smithy.kotlin.runtime.auth.awscredentials.simpleClassName -import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric +import aws.smithy.kotlin.runtime.auth.awscredentials.* import aws.smithy.kotlin.runtime.client.SdkClientOption import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.http.engine.HttpClientEngine -import aws.smithy.kotlin.runtime.serde.json.* import aws.smithy.kotlin.runtime.telemetry.logging.logger import aws.smithy.kotlin.runtime.telemetry.telemetryProvider import aws.smithy.kotlin.runtime.time.Clock @@ -123,19 +119,19 @@ public class SsoCredentialsProvider public constructor( val roleCredentials = resp.roleCredentials ?: throw CredentialsProviderException("Expected SSO roleCredentials to not be null") - return credentials( + val creds = credentials( accessKeyId = checkNotNull(roleCredentials.accessKeyId) { "Expected accessKeyId in SSO roleCredentials response" }, secretAccessKey = checkNotNull(roleCredentials.secretAccessKey) { "Expected secretAccessKey in SSO roleCredentials response" }, sessionToken = roleCredentials.sessionToken, expiration = Instant.fromEpochMilliseconds(roleCredentials.expiration), PROVIDER_NAME, accountId = accountId, - ).also { - if (ssoTokenProvider != null) { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_SSO) - } else { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_SSO_LEGACY) - } + ) + + return if (ssoTokenProvider != null) { + creds.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_SSO) + } else { + creds.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_SSO_LEGACY) } } diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProvider.kt index ee81a43db1b..d51987c3b65 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProvider.kt @@ -13,9 +13,9 @@ import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.model.PolicyDescript import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.model.RegionDisabledException import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.model.Tag import aws.sdk.kotlin.runtime.config.AwsSdkSetting -import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.* -import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.client.SdkClientOption import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.config.resolve @@ -148,9 +148,7 @@ public class StsAssumeRoleCredentialsProvider( expiration = roleCredentials.expiration, providerName = PROVIDER_NAME, accountId = accountId, - ).also { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE) - } + ).emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE) } override fun toString(): String = this.simpleClassName diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt index adf05453b68..6ef63877a24 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt @@ -11,9 +11,9 @@ import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.StsClient import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.assumeRoleWithWebIdentity import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.model.PolicyDescriptorType import aws.sdk.kotlin.runtime.config.AwsSdkSetting -import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.* -import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.client.SdkClientOption import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.config.EnvironmentSetting @@ -152,9 +152,7 @@ public class StsWebIdentityCredentialsProvider( expiration = roleCredentials.expiration, providerName = PROVIDER_NAME, accountId = accountId, - ).also { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE_WEB_ID) - } + ).emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE_WEB_ID) } override fun toString(): String = this.simpleClassName diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProvider.kt index 21aebc7c11f..7f0eb981590 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProvider.kt @@ -7,11 +7,11 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting -import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider import aws.smithy.kotlin.runtime.auth.awscredentials.simpleClassName -import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.telemetry.logging.trace import aws.smithy.kotlin.runtime.util.PlatformProvider @@ -45,9 +45,7 @@ public class SystemPropertyCredentialsProvider( sessionToken = getProperty(SESSION_TOKEN), providerName = PROVIDER_NAME, accountId = getProperty(ACCOUNT_ID), - ).also { - attributes.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_JVM_SYSTEM_PROPERTIES) - } + ).emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_JVM_SYSTEM_PROPERTIES) } override fun toString(): String = this.simpleClassName diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProviderTest.kt index 4aeb36c0097..01bd10f5cf4 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProviderTest.kt @@ -7,6 +7,8 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.util.withBusinessMetrics import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException import aws.smithy.kotlin.runtime.http.Headers @@ -46,7 +48,7 @@ class EcsCredentialsProviderTest { "test-token", expectedExpiration, "EcsContainer", - ) + ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_HTTP) private fun ecsResponse(accountId: String? = null): HttpResponse { val payload = buildJsonObject { @@ -576,7 +578,7 @@ class EcsCredentialsProviderTest { expectedExpiration, "EcsContainer", "12345", - ) + ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_HTTP) assertEquals(expected, actual) engine.assertRequests() } diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProviderTest.kt index 09ac8d1392c..9cacab24d7b 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProviderTest.kt @@ -7,6 +7,8 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.client.AwsClientOption import aws.sdk.kotlin.runtime.config.AwsSdkSetting +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.util.withBusinessMetrics import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.collections.attributesOf import io.kotest.matchers.string.shouldContain @@ -25,7 +27,15 @@ class EnvironmentCredentialsProviderTest { AwsSdkSetting.AwsSecretAccessKey.envVar to "def", AwsSdkSetting.AwsSessionToken.envVar to "ghi", ) - assertEquals(provider.resolve(), Credentials("abc", "def", "ghi", providerName = "Environment")) + assertEquals( + provider.resolve(), + Credentials( + "abc", + "def", + "ghi", + providerName = "Environment", + ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS), + ) } @Test @@ -34,7 +44,15 @@ class EnvironmentCredentialsProviderTest { AwsSdkSetting.AwsAccessKeyId.envVar to "abc", AwsSdkSetting.AwsSecretAccessKey.envVar to "def", ) - assertEquals(provider.resolve(), Credentials("abc", "def", null, providerName = "Environment")) + assertEquals( + provider.resolve(), + Credentials( + "abc", + "def", + null, + providerName = "Environment", + ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS), + ) } @Test @@ -85,7 +103,7 @@ class EnvironmentCredentialsProviderTest { "def", providerName = "Environment", attributes = attributesOf { AwsClientOption.AccountId to "12345" }, - ) + ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS) assertEquals(expected, actual) } } diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProviderTest.kt index 26e8e8f517b..fd9aad29e50 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProviderTest.kt @@ -7,6 +7,8 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.config.imds.* import aws.sdk.kotlin.runtime.config.imds.DEFAULT_TOKEN_TTL_SECONDS +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.util.withBusinessMetrics import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException import aws.smithy.kotlin.runtime.http.* @@ -126,7 +128,7 @@ class ImdsCredentialsProviderTest { "IQote///test0", expiration0, "IMDSv2", - ) + ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) assertEquals(expected0, actual0) testClock.advance(1.seconds) @@ -138,7 +140,7 @@ class ImdsCredentialsProviderTest { "IQote///test1", expiration1, "IMDSv2", - ) + ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) assertEquals(expected1, actual1) connection.assertRequests() @@ -195,7 +197,7 @@ class ImdsCredentialsProviderTest { "IQote///test", expiration, "IMDSv2", - ) + ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) assertEquals(expected, actual) connection.assertRequests() @@ -334,7 +336,7 @@ class ImdsCredentialsProviderTest { sessionToken = "IQote///test", expiration = Instant.fromEpochSeconds(1631935916), providerName = "IMDSv2", - ) + ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) assertEquals(expected, actual) @@ -402,7 +404,7 @@ class ImdsCredentialsProviderTest { sessionToken = "IQote///test", expiration = Instant.fromEpochSeconds(1631935916), providerName = "IMDSv2", - ) + ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) val provider = ImdsCredentialsProvider( profileOverride = "imds-test-role", @@ -519,7 +521,7 @@ class ImdsCredentialsProviderTest { sessionToken = "IQote///test", expiration = Instant.fromEpochSeconds(1631935916), providerName = "IMDSv2", - ) + ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) val provider = ImdsCredentialsProvider( profileOverride = "imds-test-role", diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt index b06f62e7738..23c55791472 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt @@ -7,22 +7,22 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.client.AwsClientOption -import aws.sdk.kotlin.runtime.http.interceptors.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.util.testAttributes +import aws.sdk.kotlin.runtime.util.withBusinessMetrics import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials -import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics +import aws.smithy.kotlin.runtime.auth.awscredentials.copy import aws.smithy.kotlin.runtime.collections.attributesOf -import aws.smithy.kotlin.runtime.collections.get import aws.smithy.kotlin.runtime.httptest.TestConnection import aws.smithy.kotlin.runtime.httptest.buildTestConnection import aws.smithy.kotlin.runtime.net.Host -import aws.smithy.kotlin.runtime.operation.ExecutionContext +import aws.smithy.kotlin.runtime.time.Instant import aws.smithy.kotlin.runtime.util.TestPlatformProvider import io.mockk.coEvery import io.mockk.mockkStatic import kotlinx.coroutines.test.runTest import kotlin.test.Test import kotlin.test.assertEquals -import kotlin.test.assertTrue class ProfileCredentialsProviderTest { @Test @@ -45,6 +45,7 @@ class ProfileCredentialsProviderTest { ) val actual = provider.resolve() val expected = Credentials("AKID-Default", "Default-Secret") + .withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) assertEquals(expected, actual) } @@ -73,6 +74,7 @@ class ProfileCredentialsProviderTest { ) val actual = provider.resolve() val expected = Credentials("AKID-Profile", "Profile-Secret") + .withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) assertEquals(expected, actual) } @@ -103,6 +105,7 @@ class ProfileCredentialsProviderTest { ) val actual = provider.resolve() val expected = Credentials("AKID-Profile", "Profile-Secret") + .withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) assertEquals(expected, actual) } @@ -138,7 +141,15 @@ class ProfileCredentialsProviderTest { httpClient = testEngine, ) val actual = provider.resolve() - assertEquals(StsTestUtils.CREDENTIALS, actual) + val expected = StsTestUtils.CREDENTIALS.copy( + attributes = testAttributes( + StsTestUtils.CREDENTIALS.attributes, + AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SOURCE_PROFILE, + AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE, + AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE, + ), + ) + assertEquals(expected, actual) testEngine.assertRequests() val req = testEngine.requests().first() @@ -323,6 +334,7 @@ class ProfileCredentialsProviderTest { ) val actual = provider.resolve() val expected = credentials("AKID-Default", "Default-Secret", accountId = "12345") + .withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) assertEquals(expected, actual) } @@ -352,16 +364,15 @@ class ProfileCredentialsProviderTest { platformProvider = testProvider, httpClient = testEngine, ) - val attributes = ExecutionContext() - provider.resolve(attributes) - assertTrue(attributes.contains(BusinessMetrics)) - - val actual = attributes[BusinessMetrics] - val expected = setOf( - AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_NAMED_PROVIDER.identifier, - AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS.identifier, - AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE.identifier, + val actual = provider.resolve() + val expected = StsTestUtils.CREDENTIALS.copy( + attributes = testAttributes( + StsTestUtils.CREDENTIALS.attributes, + AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_NAMED_PROVIDER, + AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS, + AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE, + ), ) assertEquals(expected, actual) } @@ -393,24 +404,27 @@ class ProfileCredentialsProviderTest { """ { "Version": 1, - "AccessKeyId": "AccessKeyId", - "SecretAccessKey": "SecretAccessKey", - "SessionToken": "SessionToken" + "AccessKeyId": "AKID-Default", + "SecretAccessKey": "Default-Secret", + "SessionToken": "SessionToken", + "Expiration" : "2019-05-29T00:21:43Z" } """.trimIndent(), ), ) - val attributes = ExecutionContext() - provider.resolve(attributes) - - assertTrue(attributes.contains(BusinessMetrics)) - - val actual = attributes[BusinessMetrics] - val expected = setOf( - AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_PROCESS.identifier, - AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS.identifier, + val actual = provider.resolve() + val expected = credentials( + "AKID-Default", + "Default-Secret", + "SessionToken", + Instant.fromIso8601("2019-05-29T00:21:43Z"), + "Process", + ).withBusinessMetrics( + AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_PROCESS, + AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS, ) + assertEquals(expected, actual) } } diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProviderTest.kt index 37f2852ea7a..2c1938f7143 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProviderTest.kt @@ -6,6 +6,8 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.util.withBusinessMetrics import aws.smithy.kotlin.runtime.http.Headers import aws.smithy.kotlin.runtime.http.HttpBody import aws.smithy.kotlin.runtime.http.HttpStatusCode @@ -201,7 +203,14 @@ class SsoCredentialsProviderTest { ) val actual = provider.resolve() - val expected = credentials("AKID", "secret", "session-token", expectedExpiration, "SSO", "123456789") + val expected = credentials( + "AKID", + "secret", + "session-token", + expectedExpiration, + "SSO", + "123456789", + ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_SSO_LEGACY) assertEquals(expected, actual) } } diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProviderTest.kt index c7b5d13815d..520ecbd378d 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProviderTest.kt @@ -6,7 +6,10 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.model.RegionDisabledException +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.util.testAttributes import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException +import aws.smithy.kotlin.runtime.auth.awscredentials.copy import aws.smithy.kotlin.runtime.http.Headers import aws.smithy.kotlin.runtime.http.HttpBody import aws.smithy.kotlin.runtime.http.HttpStatusCode @@ -49,7 +52,13 @@ class StsAssumeRoleCredentialsProviderTest { ) val actual = provider.resolve() - assertEquals(StsTestUtils.CREDENTIALS, actual) + val expected = StsTestUtils.CREDENTIALS.copy( + attributes = testAttributes( + StsTestUtils.CREDENTIALS.attributes, + AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE, + ), + ) + assertEquals(expected, actual) testEngine.assertRequests(CallAsserter.MatchingBodies) } @@ -87,7 +96,13 @@ class StsAssumeRoleCredentialsProviderTest { ) val actual = provider.resolve() - assertEquals(StsTestUtils.CREDENTIALS, actual) + val expected = StsTestUtils.CREDENTIALS.copy( + attributes = testAttributes( + StsTestUtils.CREDENTIALS.attributes, + AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE, + ), + ) + assertEquals(expected, actual) testEngine.assertRequests(CallAsserter.MatchingBodies) } @@ -163,7 +178,13 @@ class StsAssumeRoleCredentialsProviderTest { ) val actual = provider.resolve() - assertEquals(StsTestUtils.CREDENTIALS, actual) + val expected = StsTestUtils.CREDENTIALS.copy( + attributes = testAttributes( + StsTestUtils.CREDENTIALS.attributes, + AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE, + ), + ) + assertEquals(expected, actual) val req = testEngine.requests().first() assertEquals(Host.Domain("sts.amazonaws.com"), req.actual.url.host) } @@ -182,7 +203,13 @@ class StsAssumeRoleCredentialsProviderTest { ) val actual = provider.resolve() - assertEquals(StsTestUtils.CREDENTIALS, actual) + val expected = StsTestUtils.CREDENTIALS.copy( + attributes = testAttributes( + StsTestUtils.CREDENTIALS.attributes, + AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE, + ), + ) + assertEquals(expected, actual) val req = testEngine.requests().first() assertEquals(Host.Domain("sts.us-west-2.amazonaws.com"), req.actual.url.host) } diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProviderTest.kt index 2194df2593a..3a3ecc12fe1 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProviderTest.kt @@ -6,6 +6,8 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.util.withBusinessMetrics import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException import aws.smithy.kotlin.runtime.http.Headers import aws.smithy.kotlin.runtime.http.HttpBody @@ -34,7 +36,7 @@ private val CREDENTIALS = credentials( StsTestUtils.EPOCH + 15.minutes, "WebIdentityToken", "1234567", -) +).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE_WEB_ID) class StsWebIdentityCredentialsProviderTest { // see https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html#API_AssumeRoleWithWebIdentity_ResponseElements diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProviderTest.kt index 4c16609885f..16d287f0e80 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProviderTest.kt @@ -6,6 +6,8 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.util.testAttributes import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import io.kotest.matchers.string.shouldContain import kotlinx.coroutines.test.runTest @@ -23,7 +25,18 @@ class SystemPropertyCredentialsProviderTest { AwsSdkSetting.AwsSecretAccessKey.sysProp to "def", AwsSdkSetting.AwsSessionToken.sysProp to "ghi", ) - assertEquals(provider.resolve(), Credentials("abc", "def", "ghi", providerName = "SystemProperties")) + assertEquals( + provider.resolve(), + Credentials( + "abc", + "def", + "ghi", + providerName = "SystemProperties", + attributes = testAttributes( + AwsBusinessMetric.Credentials.CREDENTIALS_JVM_SYSTEM_PROPERTIES, + ), + ), + ) } @Test @@ -32,7 +45,18 @@ class SystemPropertyCredentialsProviderTest { AwsSdkSetting.AwsAccessKeyId.sysProp to "abc", AwsSdkSetting.AwsSecretAccessKey.sysProp to "def", ) - assertEquals(provider.resolve(), Credentials("abc", "def", null, providerName = "SystemProperties")) + assertEquals( + provider.resolve(), + Credentials( + "abc", + "def", + null, + providerName = "SystemProperties", + attributes = testAttributes( + AwsBusinessMetric.Credentials.CREDENTIALS_JVM_SYSTEM_PROPERTIES, + ), + ), + ) } @Test diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt new file mode 100644 index 00000000000..7901ce02ca2 --- /dev/null +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt @@ -0,0 +1,33 @@ +package aws.sdk.kotlin.runtime.util + +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetrics +import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials +import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric +import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetrics +import aws.smithy.kotlin.runtime.collections.Attributes +import aws.smithy.kotlin.runtime.collections.mutableAttributes +import aws.smithy.kotlin.runtime.collections.toMutableAttributes + +/** + * [Attributes] and any [BusinessMetric] that should be included. + */ +internal fun testAttributes(attributes: Attributes? = null, vararg metrics: BusinessMetric): Attributes { + val testAttributes = attributes?.toMutableAttributes() ?: mutableAttributes() + testAttributes.emitBusinessMetrics(metrics.toSet()) + return testAttributes +} + +/** + * [Attributes] that only contain the specified [BusinessMetric]. + */ +internal fun testAttributes(vararg metrics: BusinessMetric): Attributes { + val testAttributes = mutableAttributes() + testAttributes.emitBusinessMetrics(metrics.toSet()) + return testAttributes +} + +/** + * Type alias for [emitBusinessMetrics], used for testing. + */ +internal fun Credentials.withBusinessMetrics(vararg metrics: BusinessMetric): Credentials = + emitBusinessMetrics(metrics.toSet()) diff --git a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt index 4cc812fc4f8..5fd83621481 100644 --- a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt @@ -6,10 +6,9 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetrics import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.copy -import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics -import aws.smithy.kotlin.runtime.collections.get import aws.smithy.kotlin.runtime.httptest.TestConnection import aws.smithy.kotlin.runtime.operation.ExecutionContext import aws.smithy.kotlin.runtime.time.Instant @@ -79,7 +78,6 @@ class DefaultChainCredentialsProviderTest { override val name: String, override val docs: String, val creds: Credentials, - val businessMetrics: MutableSet, ) : TestResult() data class ErrorContains( @@ -97,15 +95,15 @@ class DefaultChainCredentialsProviderTest { return when { "Ok" in result -> { val o = checkNotNull(result["Ok"]).jsonObject - val creds = credentials( + val expectedBusinessMetrics = o["business_metrics"]?.jsonArray?.map { it.jsonPrimitive.content }?.toMutableSet() ?: mutableSetOf() + val expectedCreds = credentials( checkNotNull(o["access_key_id"]).jsonPrimitive.content, checkNotNull(o["secret_access_key"]).jsonPrimitive.content, o["session_token"]?.jsonPrimitive?.content, o["expiry"]?.jsonPrimitive?.longOrNull?.let { Instant.fromEpochSeconds(it) }, accountId = o["accountId"]?.jsonPrimitive?.content, - ) - val businessMetrics = o["business_metrics"]?.jsonArray?.map { it.jsonPrimitive.content }?.toMutableSet() ?: mutableSetOf() - Ok(name, docs, creds, businessMetrics) + ).emitBusinessMetrics(expectedBusinessMetrics) + Ok(name, docs, expectedCreds) } "ErrorContains" in result -> ErrorContains(name, docs, checkNotNull(result["ErrorContains"]).jsonPrimitive.content) else -> error("unrecognized result object: $result") @@ -183,10 +181,6 @@ class DefaultChainCredentialsProviderTest { val creds = actualCreds.copy(providerName = null, expiration = sanitizedExpiration) assertEquals(expected.creds, creds) - if (expected.businessMetrics.isNotEmpty()) { - assertEquals(expected.businessMetrics, attributes[BusinessMetrics]) - } - // assert http traffic to the extent we can. These tests do not have specific timestamps they // were signed with and some lack enough context to even assert a body (e.g. incorrect content-type). // They would require additional metadata to make use of `testEngine.assertRequests()`. diff --git a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProviderTest.kt b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProviderTest.kt index 6752529dd13..e8f88441286 100644 --- a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProviderTest.kt @@ -5,6 +5,8 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.util.withBusinessMetrics import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException import aws.smithy.kotlin.runtime.time.Instant @@ -41,7 +43,7 @@ class ProcessCredentialsProviderTest { sessionToken = "SessionToken", expiration = Instant.fromEpochSeconds(1665705600), providerName = "Process", - ) + ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS) val processCredentialsProvider = ProcessCredentialsProvider("anyString") val actualCredentials = processCredentialsProvider.resolve() @@ -71,7 +73,7 @@ class ProcessCredentialsProviderTest { sessionToken = "SessionToken", expiration = Instant.MAX_VALUE, providerName = "Process", - ) + ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS) val processCredentialsProvider = ProcessCredentialsProvider("anyString") val actualCredentials = processCredentialsProvider.resolve() @@ -164,7 +166,7 @@ class ProcessCredentialsProviderTest { expiration = Instant.fromEpochSeconds(1665705600), providerName = "Process", accountId = "12345", - ) + ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS) val processCredentialsProvider = ProcessCredentialsProvider("anyString") val actualCredentials = processCredentialsProvider.resolve() diff --git a/aws-runtime/aws-http/api/aws-http.api b/aws-runtime/aws-http/api/aws-http.api index 3a236a76aed..cee666c0b42 100644 --- a/aws-runtime/aws-http/api/aws-http.api +++ b/aws-runtime/aws-http/api/aws-http.api @@ -140,39 +140,6 @@ public final class aws/sdk/kotlin/runtime/http/interceptors/AddUserAgentMetadata public fun readBeforeTransmit (Laws/smithy/kotlin/runtime/client/ProtocolRequestInterceptorContext;)V } -public final class aws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric : java/lang/Enum, aws/smithy/kotlin/runtime/businessmetrics/BusinessMetric { - public static final field S3_EXPRESS_BUCKET Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public fun getIdentifier ()Ljava/lang/String; - public static fun valueOf (Ljava/lang/String;)Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric; - public static fun values ()[Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric; -} - -public final class aws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials : java/lang/Enum, aws/smithy/kotlin/runtime/businessmetrics/BusinessMetric { - public static final field CREDENTIALS_CODE Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static final field CREDENTIALS_ENV_VARS Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static final field CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static final field CREDENTIALS_HTTP Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static final field CREDENTIALS_IMDS Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static final field CREDENTIALS_JVM_SYSTEM_PROPERTIES Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static final field CREDENTIALS_PROCESS Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static final field CREDENTIALS_PROFILE Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static final field CREDENTIALS_PROFILE_NAMED_PROVIDER Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static final field CREDENTIALS_PROFILE_PROCESS Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static final field CREDENTIALS_PROFILE_SOURCE_PROFILE Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static final field CREDENTIALS_PROFILE_SSO Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static final field CREDENTIALS_PROFILE_SSO_LEGACY Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static final field CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static final field CREDENTIALS_SSO Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static final field CREDENTIALS_SSO_LEGACY Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static final field CREDENTIALS_STS_ASSUME_ROLE Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static final field CREDENTIALS_STS_ASSUME_ROLE_WEB_ID Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static fun getEntries ()Lkotlin/enums/EnumEntries; - public fun getIdentifier ()Ljava/lang/String; - public static fun valueOf (Ljava/lang/String;)Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; - public static fun values ()[Laws/sdk/kotlin/runtime/http/interceptors/AwsBusinessMetric$Credentials; -} - public final class aws/sdk/kotlin/runtime/http/interceptors/AwsSpanInterceptor : aws/smithy/kotlin/runtime/client/Interceptor { public static final field INSTANCE Laws/sdk/kotlin/runtime/http/interceptors/AwsSpanInterceptor; public fun modifyBeforeAttemptCompletion-gIAlu-s (Laws/smithy/kotlin/runtime/client/ResponseInterceptorContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -196,7 +163,7 @@ public final class aws/sdk/kotlin/runtime/http/interceptors/AwsSpanInterceptor : public fun readBeforeTransmit (Laws/smithy/kotlin/runtime/client/ProtocolRequestInterceptorContext;)V } -public final class aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptor : aws/smithy/kotlin/runtime/client/Interceptor { +public final class aws/sdk/kotlin/runtime/http/interceptors/UnsupportedSigningAlgorithmInterceptor : aws/smithy/kotlin/runtime/client/Interceptor { public fun ()V public fun modifyBeforeAttemptCompletion-gIAlu-s (Laws/smithy/kotlin/runtime/client/ResponseInterceptorContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun modifyBeforeCompletion-gIAlu-s (Laws/smithy/kotlin/runtime/client/ResponseInterceptorContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -219,7 +186,47 @@ public final class aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInter public fun readBeforeTransmit (Laws/smithy/kotlin/runtime/client/ProtocolRequestInterceptorContext;)V } -public final class aws/sdk/kotlin/runtime/http/interceptors/UnsupportedSigningAlgorithmInterceptor : aws/smithy/kotlin/runtime/client/Interceptor { +public final class aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric : java/lang/Enum, aws/smithy/kotlin/runtime/businessmetrics/BusinessMetric { + public static final field S3_EXPRESS_BUCKET Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public fun getIdentifier ()Ljava/lang/String; + public static fun valueOf (Ljava/lang/String;)Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric; + public static fun values ()[Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric; +} + +public final class aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials : java/lang/Enum, aws/smithy/kotlin/runtime/businessmetrics/BusinessMetric { + public static final field CREDENTIALS_CODE Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_ENV_VARS Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_HTTP Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_IMDS Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_JVM_SYSTEM_PROPERTIES Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_PROCESS Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_PROFILE Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_PROFILE_NAMED_PROVIDER Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_PROFILE_PROCESS Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_PROFILE_SOURCE_PROFILE Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_PROFILE_SSO Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_PROFILE_SSO_LEGACY Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_SSO Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_SSO_LEGACY Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_STS_ASSUME_ROLE Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static final field CREDENTIALS_STS_ASSUME_ROLE_WEB_ID Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public fun getIdentifier ()Ljava/lang/String; + public static fun valueOf (Ljava/lang/String;)Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; + public static fun values ()[Laws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetric$Credentials; +} + +public final class aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtilsKt { + public static final fun emitBusinessMetric (Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetric;)Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials; + public static final fun emitBusinessMetric (Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials;Ljava/lang/String;)Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials; + public static final fun emitBusinessMetricsWithSetOfBusinessMetrics (Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials;Ljava/util/Set;)Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials; + public static final fun emitBusinessMetricsWithSetOfString (Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials;Ljava/util/Set;)Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials; +} + +public final class aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/BusinessMetricsInterceptor : aws/smithy/kotlin/runtime/client/Interceptor { public fun ()V public fun modifyBeforeAttemptCompletion-gIAlu-s (Laws/smithy/kotlin/runtime/client/ResponseInterceptorContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun modifyBeforeCompletion-gIAlu-s (Laws/smithy/kotlin/runtime/client/ResponseInterceptorContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptor.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptor.kt deleted file mode 100644 index 6b5d96cbd8c..00000000000 --- a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptor.kt +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -package aws.sdk.kotlin.runtime.http.interceptors - -import aws.sdk.kotlin.runtime.http.BUSINESS_METRICS_MAX_LENGTH -import aws.sdk.kotlin.runtime.http.middleware.USER_AGENT -import aws.smithy.kotlin.runtime.InternalApi -import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric -import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics -import aws.smithy.kotlin.runtime.client.ProtocolRequestInterceptorContext -import aws.smithy.kotlin.runtime.http.interceptors.HttpInterceptor -import aws.smithy.kotlin.runtime.http.request.HttpRequest -import aws.smithy.kotlin.runtime.http.request.toBuilder - -/** - * Appends business metrics to the `User-Agent` header. - */ -public class BusinessMetricsInterceptor : HttpInterceptor { - override suspend fun modifyBeforeTransmit(context: ProtocolRequestInterceptorContext): HttpRequest { - context.executionContext.getOrNull(BusinessMetrics)?.let { metrics -> - val metricsString = formatMetrics(metrics) - val currentUserAgentHeader = context.protocolRequest.headers[USER_AGENT] - val modifiedRequest = context.protocolRequest.toBuilder() - - modifiedRequest.headers[USER_AGENT] = currentUserAgentHeader + metricsString - - return modifiedRequest.build() - } - return context.protocolRequest - } -} - -/** - * Makes sure the metrics do not exceed the maximum size and truncates them if so. - */ -private fun formatMetrics(metrics: MutableSet): String { - if (metrics.isEmpty()) return "" - val metricsString = metrics.joinToString(",", "m/") - val metricsByteArray = metricsString.encodeToByteArray() - - if (metricsByteArray.size <= BUSINESS_METRICS_MAX_LENGTH) return metricsString - - val lastCommaIndex = metricsByteArray - .sliceArray(0 until 1024) - .indexOfLast { it == ','.code.toByte() } - .takeIf { it != -1 } - - lastCommaIndex?.let { - return metricsByteArray.decodeToString( - 0, - lastCommaIndex, - true, - ) - } - - throw IllegalStateException("Business metrics are incorrectly formatted: $metricsString") -} - -/** - * AWS SDK specific business metrics - */ -@InternalApi -public enum class AwsBusinessMetric(public override val identifier: String) : BusinessMetric { - S3_EXPRESS_BUCKET("J"), - ; - - public enum class Credentials(public override val identifier: String) : BusinessMetric { - CREDENTIALS_CODE("e"), - CREDENTIALS_JVM_SYSTEM_PROPERTIES("f"), - CREDENTIALS_ENV_VARS("g"), - CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN("h"), - CREDENTIALS_STS_ASSUME_ROLE("i"), - CREDENTIALS_STS_ASSUME_ROLE_WEB_ID("k"), - CREDENTIALS_PROFILE("n"), - CREDENTIALS_PROFILE_SOURCE_PROFILE("o"), - CREDENTIALS_PROFILE_NAMED_PROVIDER("p"), - CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN("q"), - CREDENTIALS_PROFILE_SSO("r"), - CREDENTIALS_SSO("s"), - CREDENTIALS_PROFILE_SSO_LEGACY("t"), - CREDENTIALS_SSO_LEGACY("u"), - CREDENTIALS_PROFILE_PROCESS("v"), - CREDENTIALS_PROCESS("w"), - CREDENTIALS_HTTP("z"), - CREDENTIALS_IMDS("0"), - } -} diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt new file mode 100644 index 00000000000..b316820d32a --- /dev/null +++ b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt @@ -0,0 +1,117 @@ +package aws.sdk.kotlin.runtime.http.interceptors.businessmetrics + +import aws.sdk.kotlin.runtime.http.BUSINESS_METRICS_MAX_LENGTH +import aws.smithy.kotlin.runtime.InternalApi +import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials +import aws.smithy.kotlin.runtime.auth.awscredentials.copy +import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric +import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric +import aws.smithy.kotlin.runtime.collections.MutableAttributes +import aws.smithy.kotlin.runtime.collections.toMutableAttributes + +/** + * Makes sure the metrics do not exceed the maximum size and truncates them if so. + */ +internal fun formatMetrics(metrics: MutableSet): String { + if (metrics.isEmpty()) return "" + val metricsString = metrics.joinToString(",", "m/") + val metricsByteArray = metricsString.encodeToByteArray() + + if (metricsByteArray.size <= BUSINESS_METRICS_MAX_LENGTH) return metricsString + + val lastCommaIndex = metricsByteArray + .sliceArray(0 until 1024) + .indexOfLast { it == ','.code.toByte() } + .takeIf { it != -1 } + + lastCommaIndex?.let { + return metricsByteArray.decodeToString( + 0, + lastCommaIndex, + true, + ) + } + + throw IllegalStateException("Business metrics are incorrectly formatted: $metricsString") +} + +/** + * AWS SDK specific business metrics + */ +@InternalApi +public enum class AwsBusinessMetric(public override val identifier: String) : BusinessMetric { + S3_EXPRESS_BUCKET("J"), + ; + + @InternalApi + public enum class Credentials(public override val identifier: String) : BusinessMetric { + CREDENTIALS_CODE("e"), + CREDENTIALS_JVM_SYSTEM_PROPERTIES("f"), + CREDENTIALS_ENV_VARS("g"), + CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN("h"), + CREDENTIALS_STS_ASSUME_ROLE("i"), + CREDENTIALS_STS_ASSUME_ROLE_WEB_ID("k"), + CREDENTIALS_PROFILE("n"), + CREDENTIALS_PROFILE_SOURCE_PROFILE("o"), + CREDENTIALS_PROFILE_NAMED_PROVIDER("p"), + CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN("q"), + CREDENTIALS_PROFILE_SSO("r"), + CREDENTIALS_SSO("s"), + CREDENTIALS_PROFILE_SSO_LEGACY("t"), + CREDENTIALS_SSO_LEGACY("u"), + CREDENTIALS_PROFILE_PROCESS("v"), + CREDENTIALS_PROCESS("w"), + CREDENTIALS_HTTP("z"), + CREDENTIALS_IMDS("0"), + } +} + +/** + * Emits a business metric into [Credentials.attributes] + * @param identifier The identifier of the [BusinessMetric] to be emitted. + */ +@InternalApi +public fun Credentials.emitBusinessMetric(identifier: String): Credentials = + when (val credentialsAttributes = this.attributes) { + is MutableAttributes -> { + credentialsAttributes.emitBusinessMetric(identifier) + this + } + else -> { + val newCredentialsAttributes = credentialsAttributes.toMutableAttributes() + newCredentialsAttributes.emitBusinessMetric(identifier) + this.copy(attributes = newCredentialsAttributes) + } + } + +/** + * Emits a business metric into [Credentials.attributes] + * @param metric The [BusinessMetric] to be emitted. + */ +@InternalApi +public fun Credentials.emitBusinessMetric(metric: BusinessMetric): Credentials = this.emitBusinessMetric(metric.identifier) + +/** + * Emits business metrics into [Credentials.attributes] + * @param identifiers The identifiers of the [BusinessMetric]s to be emitted. + */ +@InternalApi +@JvmName("emitBusinessMetricsWithSetOfString") +public fun Credentials.emitBusinessMetrics(identifiers: Set): Credentials { + var credentials = this + identifiers.forEach { identifier -> + credentials = this.emitBusinessMetric(identifier) + } + return credentials +} + +/** + * Emits business metrics into [Credentials.attributes] + * @param metrics The [BusinessMetric]s to be emitted. + */ +@InternalApi +@JvmName("emitBusinessMetricsWithSetOfBusinessMetrics") +public fun Credentials.emitBusinessMetrics(metrics: Set): Credentials = + this.emitBusinessMetrics( + metrics.map { it.identifier }.toSet(), + ) diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/BusinessMetricsInterceptor.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/BusinessMetricsInterceptor.kt new file mode 100644 index 00000000000..43bed2aecf1 --- /dev/null +++ b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/BusinessMetricsInterceptor.kt @@ -0,0 +1,30 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.runtime.http.interceptors.businessmetrics + +import aws.sdk.kotlin.runtime.http.middleware.USER_AGENT +import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics +import aws.smithy.kotlin.runtime.client.ProtocolRequestInterceptorContext +import aws.smithy.kotlin.runtime.http.interceptors.HttpInterceptor +import aws.smithy.kotlin.runtime.http.request.HttpRequest +import aws.smithy.kotlin.runtime.http.request.toBuilder + +/** + * Appends business metrics to the `User-Agent` header. + */ +public class BusinessMetricsInterceptor : HttpInterceptor { + override suspend fun modifyBeforeTransmit(context: ProtocolRequestInterceptorContext): HttpRequest { + context.executionContext.getOrNull(BusinessMetrics)?.let { metrics -> + val metricsString = formatMetrics(metrics) + val currentUserAgentHeader = context.protocolRequest.headers[USER_AGENT] + val modifiedRequest = context.protocolRequest.toBuilder() + + modifiedRequest.headers[USER_AGENT] = currentUserAgentHeader + metricsString + + return modifiedRequest.build() + } + return context.protocolRequest + } +} diff --git a/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptorTest.kt b/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptorTest.kt index 8469d6fd4ea..238f0dc8f48 100644 --- a/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptorTest.kt +++ b/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptorTest.kt @@ -5,6 +5,8 @@ package aws.sdk.kotlin.runtime.http.interceptors import aws.sdk.kotlin.runtime.http.BUSINESS_METRICS_MAX_LENGTH +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.BusinessMetricsInterceptor import aws.sdk.kotlin.runtime.http.middleware.USER_AGENT import aws.smithy.kotlin.runtime.businessmetrics.SmithyBusinessMetric import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric diff --git a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/AwsRuntimeTypes.kt b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/AwsRuntimeTypes.kt index b4debfc4bd8..2246bdd13f7 100644 --- a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/AwsRuntimeTypes.kt +++ b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/AwsRuntimeTypes.kt @@ -60,8 +60,11 @@ object AwsRuntimeTypes { object Interceptors : RuntimeTypePackage(AwsKotlinDependency.AWS_HTTP, "interceptors") { val AddUserAgentMetadataInterceptor = symbol("AddUserAgentMetadataInterceptor") val UnsupportedSigningAlgorithmInterceptor = symbol("UnsupportedSigningAlgorithmInterceptor") - val BusinessMetricsInterceptor = symbol("BusinessMetricsInterceptor") - val AwsBusinessMetric = symbol("AwsBusinessMetric") + + object BusinessMetrics : RuntimeTypePackage(AwsKotlinDependency.AWS_HTTP, "interceptors.businessmetrics") { + val BusinessMetricsInterceptor = symbol("BusinessMetricsInterceptor") + val AwsBusinessMetric = symbol("AwsBusinessMetric") + } } object Retries { diff --git a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt index 75b07147fa9..71882150a3e 100644 --- a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt +++ b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt @@ -17,7 +17,7 @@ import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolMiddlewa import software.amazon.smithy.model.shapes.OperationShape /** - * Renders the addition of the [BusinessMetricsInterceptor] and endpoint business metrics emitters + * Renders the addition of the [BusinessMetricsInterceptor], endpoint business metrics emitters, and a static credentials business metric. */ class BusinessMetricsIntegration : KotlinIntegration { override val order: Byte @@ -47,7 +47,7 @@ class BusinessMetricsIntegration : KotlinIntegration { AwsSigningAttributes, AwsSigningAttributes, RuntimeTypes.Core.BusinessMetrics.emitBusinessMetric, - AwsRuntimeTypes.Http.Interceptors.AwsBusinessMetric, + AwsRuntimeTypes.Http.Interceptors.BusinessMetrics.AwsBusinessMetric, ) writer.write("") } @@ -62,7 +62,7 @@ class BusinessMetricsIntegration : KotlinIntegration { override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) { writer.write( "op.interceptors.add(#T())", - AwsRuntimeTypes.Http.Interceptors.BusinessMetricsInterceptor, + AwsRuntimeTypes.Http.Interceptors.BusinessMetrics.BusinessMetricsInterceptor, ) } } @@ -78,7 +78,7 @@ class BusinessMetricsIntegration : KotlinIntegration { write( "op.context.#T(#T.Credentials.CREDENTIALS_CODE)", RuntimeTypes.Core.BusinessMetrics.emitBusinessMetric, - AwsRuntimeTypes.Http.Interceptors.AwsBusinessMetric, + AwsRuntimeTypes.Http.Interceptors.BusinessMetrics.AwsBusinessMetric, ) } } From 02db28ddc186e4eab1dc42edb523dbd9b390a4fc Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 31 Oct 2024 17:24:53 -0400 Subject: [PATCH 15/20] Add JvmName import that didn't cause build failures when missing locally for some reason 0_o --- .../http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt index b316820d32a..b9fdbbc4f38 100644 --- a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt +++ b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt @@ -8,6 +8,7 @@ import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.MutableAttributes import aws.smithy.kotlin.runtime.collections.toMutableAttributes +import kotlin.jvm.JvmName /** * Makes sure the metrics do not exceed the maximum size and truncates them if so. From b74a50b1a95c875fcc878088634048ed1ca1eb53 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 31 Oct 2024 19:48:50 -0400 Subject: [PATCH 16/20] Remove breaking changes from smithy kotlin --- .../kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt index 7901ce02ca2..6cde8ecc169 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt @@ -3,7 +3,7 @@ package aws.sdk.kotlin.runtime.util import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetrics import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric -import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetrics +import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.collections.mutableAttributes import aws.smithy.kotlin.runtime.collections.toMutableAttributes @@ -13,7 +13,9 @@ import aws.smithy.kotlin.runtime.collections.toMutableAttributes */ internal fun testAttributes(attributes: Attributes? = null, vararg metrics: BusinessMetric): Attributes { val testAttributes = attributes?.toMutableAttributes() ?: mutableAttributes() - testAttributes.emitBusinessMetrics(metrics.toSet()) + metrics.forEach { metric -> + testAttributes.emitBusinessMetric(metric.identifier) + } return testAttributes } @@ -22,7 +24,9 @@ internal fun testAttributes(attributes: Attributes? = null, vararg metrics: Busi */ internal fun testAttributes(vararg metrics: BusinessMetric): Attributes { val testAttributes = mutableAttributes() - testAttributes.emitBusinessMetrics(metrics.toSet()) + metrics.forEach { metric -> + testAttributes.emitBusinessMetric(metric.identifier) + } return testAttributes } From 864dc652c7c64d943bbd71c8b26dea5cc00a7379 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 31 Oct 2024 20:58:05 -0400 Subject: [PATCH 17/20] Set -> Set business metrics --- .../credentials/ProfileCredentialsProvider.kt | 17 +++++---- .../util/AwsBusinessMetricsTestUtils.kt | 8 +++- .../DefaultChainCredentialsProviderTest.kt | 3 +- aws-runtime/aws-http/api/aws-http.api | 4 +- .../AwsBusinessMetricsUtils.kt | 38 +++++-------------- .../BusinessMetricsInterceptorTest.kt | 24 +++--------- 6 files changed, 33 insertions(+), 61 deletions(-) diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt index fba61523c2e..ce1534947b3 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt @@ -19,6 +19,7 @@ import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetri import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetrics import aws.sdk.kotlin.runtime.region.resolveRegion import aws.smithy.kotlin.runtime.auth.awscredentials.* +import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.http.engine.HttpClientEngine @@ -87,7 +88,7 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( public val httpClient: HttpClientEngine? = null, public val configurationSource: AwsConfigurationSource? = null, ) : CloseableCredentialsProvider { - private val credentialsBusinessMetrics: MutableSet = mutableSetOf() + private val credentialsBusinessMetrics: MutableSet = mutableSetOf() public constructor( profileName: String? = null, @@ -134,7 +135,7 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( chain.roles.forEach { roleArn -> logger.debug { "Assuming role `${roleArn.roleArn}`" } if (roleArn.source == RoleArnSource.SOURCE_PROFILE) { - credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SOURCE_PROFILE.identifier) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SOURCE_PROFILE) } val assumeProvider = roleArn.toCredentialsProvider(creds, region) @@ -159,11 +160,11 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( private suspend fun LeafProvider.toCredentialsProvider(region: LazyAsyncValue): CredentialsProvider = when (this) { is LeafProvider.NamedSource -> namedProviders[name].also { - credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_NAMED_PROVIDER.identifier) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_NAMED_PROVIDER) } ?: throw ProviderConfigurationException("unknown credentials source: $name") is LeafProvider.AccessKey -> StaticCredentialsProvider(credentials).also { - credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE.identifier) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) } is LeafProvider.WebIdentityTokenRole -> StsWebIdentityCredentialsProvider( @@ -174,7 +175,7 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( platformProvider = platformProvider, httpClient = httpClient, ).also { - credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN.identifier) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN) } is LeafProvider.SsoSession -> SsoCredentialsProvider( @@ -186,7 +187,7 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( httpClient = httpClient, platformProvider = platformProvider, ).also { - credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO.identifier) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO) } is LeafProvider.LegacySso -> SsoCredentialsProvider( @@ -197,11 +198,11 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( httpClient = httpClient, platformProvider = platformProvider, ).also { - credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO_LEGACY.identifier) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_SSO_LEGACY) } is LeafProvider.Process -> ProcessCredentialsProvider(command).also { - credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_PROCESS.identifier) + credentialsBusinessMetrics.add(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_PROCESS) } } diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt index 6cde8ecc169..ed78b9a3449 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt @@ -1,5 +1,6 @@ package aws.sdk.kotlin.runtime.util +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetrics import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric @@ -14,7 +15,7 @@ import aws.smithy.kotlin.runtime.collections.toMutableAttributes internal fun testAttributes(attributes: Attributes? = null, vararg metrics: BusinessMetric): Attributes { val testAttributes = attributes?.toMutableAttributes() ?: mutableAttributes() metrics.forEach { metric -> - testAttributes.emitBusinessMetric(metric.identifier) + testAttributes.emitBusinessMetric(metric) } return testAttributes } @@ -25,7 +26,7 @@ internal fun testAttributes(attributes: Attributes? = null, vararg metrics: Busi internal fun testAttributes(vararg metrics: BusinessMetric): Attributes { val testAttributes = mutableAttributes() metrics.forEach { metric -> - testAttributes.emitBusinessMetric(metric.identifier) + testAttributes.emitBusinessMetric(metric) } return testAttributes } @@ -35,3 +36,6 @@ internal fun testAttributes(vararg metrics: BusinessMetric): Attributes { */ internal fun Credentials.withBusinessMetrics(vararg metrics: BusinessMetric): Credentials = emitBusinessMetrics(metrics.toSet()) + +internal fun String.toAwsBusinessMetric(): BusinessMetric = + AwsBusinessMetric.Credentials.entries.find { it.identifier == this } ?: throw Exception("String '$this' is not an AWS business metric") diff --git a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt index 5fd83621481..203dea7e773 100644 --- a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt @@ -7,6 +7,7 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetrics +import aws.sdk.kotlin.runtime.util.toAwsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.copy import aws.smithy.kotlin.runtime.httptest.TestConnection @@ -102,7 +103,7 @@ class DefaultChainCredentialsProviderTest { o["session_token"]?.jsonPrimitive?.content, o["expiry"]?.jsonPrimitive?.longOrNull?.let { Instant.fromEpochSeconds(it) }, accountId = o["accountId"]?.jsonPrimitive?.content, - ).emitBusinessMetrics(expectedBusinessMetrics) + ).emitBusinessMetrics(expectedBusinessMetrics.map { it.toAwsBusinessMetric() }.toSet()) Ok(name, docs, expectedCreds) } "ErrorContains" in result -> ErrorContains(name, docs, checkNotNull(result["ErrorContains"]).jsonPrimitive.content) diff --git a/aws-runtime/aws-http/api/aws-http.api b/aws-runtime/aws-http/api/aws-http.api index cee666c0b42..4e4a66587ca 100644 --- a/aws-runtime/aws-http/api/aws-http.api +++ b/aws-runtime/aws-http/api/aws-http.api @@ -221,9 +221,7 @@ public final class aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsB public final class aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtilsKt { public static final fun emitBusinessMetric (Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetric;)Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials; - public static final fun emitBusinessMetric (Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials;Ljava/lang/String;)Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials; - public static final fun emitBusinessMetricsWithSetOfBusinessMetrics (Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials;Ljava/util/Set;)Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials; - public static final fun emitBusinessMetricsWithSetOfString (Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials;Ljava/util/Set;)Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials; + public static final fun emitBusinessMetrics (Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials;Ljava/util/Set;)Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials; } public final class aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/BusinessMetricsInterceptor : aws/smithy/kotlin/runtime/client/Interceptor { diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt index b9fdbbc4f38..cfb838c074e 100644 --- a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt +++ b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt @@ -8,14 +8,13 @@ import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.MutableAttributes import aws.smithy.kotlin.runtime.collections.toMutableAttributes -import kotlin.jvm.JvmName /** * Makes sure the metrics do not exceed the maximum size and truncates them if so. */ -internal fun formatMetrics(metrics: MutableSet): String { +internal fun formatMetrics(metrics: MutableSet): String { if (metrics.isEmpty()) return "" - val metricsString = metrics.joinToString(",", "m/") + val metricsString = metrics.joinToString(",", "m/") { it.identifier } val metricsByteArray = metricsString.encodeToByteArray() if (metricsByteArray.size <= BUSINESS_METRICS_MAX_LENGTH) return metricsString @@ -69,50 +68,31 @@ public enum class AwsBusinessMetric(public override val identifier: String) : Bu /** * Emits a business metric into [Credentials.attributes] - * @param identifier The identifier of the [BusinessMetric] to be emitted. + * @param metric The [BusinessMetric] to be emitted. */ @InternalApi -public fun Credentials.emitBusinessMetric(identifier: String): Credentials = +public fun Credentials.emitBusinessMetric(metric: BusinessMetric): Credentials = when (val credentialsAttributes = this.attributes) { is MutableAttributes -> { - credentialsAttributes.emitBusinessMetric(identifier) + credentialsAttributes.emitBusinessMetric(metric) this } else -> { val newCredentialsAttributes = credentialsAttributes.toMutableAttributes() - newCredentialsAttributes.emitBusinessMetric(identifier) + newCredentialsAttributes.emitBusinessMetric(metric) this.copy(attributes = newCredentialsAttributes) } } -/** - * Emits a business metric into [Credentials.attributes] - * @param metric The [BusinessMetric] to be emitted. - */ -@InternalApi -public fun Credentials.emitBusinessMetric(metric: BusinessMetric): Credentials = this.emitBusinessMetric(metric.identifier) - /** * Emits business metrics into [Credentials.attributes] - * @param identifiers The identifiers of the [BusinessMetric]s to be emitted. + * @param metrics The [BusinessMetric]s to be emitted. */ @InternalApi -@JvmName("emitBusinessMetricsWithSetOfString") -public fun Credentials.emitBusinessMetrics(identifiers: Set): Credentials { +public fun Credentials.emitBusinessMetrics(metrics: Set): Credentials { var credentials = this - identifiers.forEach { identifier -> + metrics.forEach { identifier -> credentials = this.emitBusinessMetric(identifier) } return credentials } - -/** - * Emits business metrics into [Credentials.attributes] - * @param metrics The [BusinessMetric]s to be emitted. - */ -@InternalApi -@JvmName("emitBusinessMetricsWithSetOfBusinessMetrics") -public fun Credentials.emitBusinessMetrics(metrics: Set): Credentials = - this.emitBusinessMetrics( - metrics.map { it.identifier }.toSet(), - ) diff --git a/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptorTest.kt b/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptorTest.kt index 238f0dc8f48..cd8c3e6607e 100644 --- a/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptorTest.kt +++ b/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptorTest.kt @@ -8,6 +8,7 @@ import aws.sdk.kotlin.runtime.http.BUSINESS_METRICS_MAX_LENGTH import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.BusinessMetricsInterceptor import aws.sdk.kotlin.runtime.http.middleware.USER_AGENT +import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric import aws.smithy.kotlin.runtime.businessmetrics.SmithyBusinessMetric import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.client.ProtocolRequestInterceptorContext @@ -18,7 +19,6 @@ import aws.smithy.kotlin.runtime.net.url.Url import aws.smithy.kotlin.runtime.operation.ExecutionContext import kotlinx.coroutines.test.runTest import kotlin.test.Test -import kotlin.test.assertFailsWith import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -69,10 +69,13 @@ class BusinessMetricsInterceptorTest { @Test fun truncateBusinessMetrics() = runTest { val executionContext = ExecutionContext() - executionContext.attributes[aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics] = mutableSetOf() for (i in 0..1024) { - executionContext.attributes[aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics].add(i.toString()) + executionContext.emitBusinessMetric( + object : BusinessMetric { + override val identifier: String = i.toString() + }, + ) } val rawMetrics = executionContext[aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics] @@ -89,21 +92,6 @@ class BusinessMetricsInterceptorTest { assertTrue(truncatedMetrics.encodeToByteArray().size <= BUSINESS_METRICS_MAX_LENGTH) assertFalse(truncatedMetrics.endsWith(",")) } - - @Test - fun malformedBusinessMetrics() = runTest { - val executionContext = ExecutionContext() - - executionContext.attributes[aws.smithy.kotlin.runtime.businessmetrics.BusinessMetrics] = mutableSetOf( - "A".repeat(BUSINESS_METRICS_MAX_LENGTH), - ) - - val interceptor = BusinessMetricsInterceptor() - - assertFailsWith("Business metrics are incorrectly formatted:") { - interceptor.modifyBeforeTransmit(interceptorContext(executionContext)) - } - } } private fun interceptorContext(executionContext: ExecutionContext): ProtocolRequestInterceptorContext = From 82c7a3fe2bbba700b747ce2192b31d813f4dc9d2 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 31 Oct 2024 21:10:35 -0400 Subject: [PATCH 18/20] Self review --- .../util/AwsBusinessMetricsTestUtils.kt | 2 +- .../DefaultChainCredentialsProviderTest.kt | 6 ++++-- .../BusinessMetricsInterceptorTest.kt | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt index ed78b9a3449..e3d7fc86a72 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt @@ -37,5 +37,5 @@ internal fun testAttributes(vararg metrics: BusinessMetric): Attributes { internal fun Credentials.withBusinessMetrics(vararg metrics: BusinessMetric): Credentials = emitBusinessMetrics(metrics.toSet()) -internal fun String.toAwsBusinessMetric(): BusinessMetric = +internal fun String.toAwsCredentialsBusinessMetric(): BusinessMetric = AwsBusinessMetric.Credentials.entries.find { it.identifier == this } ?: throw Exception("String '$this' is not an AWS business metric") diff --git a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt index 203dea7e773..f0603fba5a0 100644 --- a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt @@ -7,7 +7,7 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetrics -import aws.sdk.kotlin.runtime.util.toAwsBusinessMetric +import aws.sdk.kotlin.runtime.util.toAwsCredentialsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.copy import aws.smithy.kotlin.runtime.httptest.TestConnection @@ -103,7 +103,9 @@ class DefaultChainCredentialsProviderTest { o["session_token"]?.jsonPrimitive?.content, o["expiry"]?.jsonPrimitive?.longOrNull?.let { Instant.fromEpochSeconds(it) }, accountId = o["accountId"]?.jsonPrimitive?.content, - ).emitBusinessMetrics(expectedBusinessMetrics.map { it.toAwsBusinessMetric() }.toSet()) + ).emitBusinessMetrics( + expectedBusinessMetrics.map { it.toAwsCredentialsBusinessMetric() }.toSet(), + ) Ok(name, docs, expectedCreds) } "ErrorContains" in result -> ErrorContains(name, docs, checkNotNull(result["ErrorContains"]).jsonPrimitive.content) diff --git a/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptorTest.kt b/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptorTest.kt index cd8c3e6607e..b38bdc86732 100644 --- a/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptorTest.kt +++ b/aws-runtime/aws-http/common/test/aws/sdk/kotlin/runtime/http/interceptors/BusinessMetricsInterceptorTest.kt @@ -19,6 +19,7 @@ import aws.smithy.kotlin.runtime.net.url.Url import aws.smithy.kotlin.runtime.operation.ExecutionContext import kotlinx.coroutines.test.runTest import kotlin.test.Test +import kotlin.test.assertFailsWith import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -92,6 +93,24 @@ class BusinessMetricsInterceptorTest { assertTrue(truncatedMetrics.encodeToByteArray().size <= BUSINESS_METRICS_MAX_LENGTH) assertFalse(truncatedMetrics.endsWith(",")) } + + @Test + fun malformedBusinessMetrics() = runTest { + val executionContext = ExecutionContext() + val reallyLongMetric = "All work and no play makes Jack a dull boy".repeat(1000) + + executionContext.attributes.emitBusinessMetric( + object : BusinessMetric { + override val identifier: String = reallyLongMetric + }, + ) + + val interceptor = BusinessMetricsInterceptor() + + assertFailsWith("Business metrics are incorrectly formatted:") { + interceptor.modifyBeforeTransmit(interceptorContext(executionContext)) + } + } } private fun interceptorContext(executionContext: ExecutionContext): ProtocolRequestInterceptorContext = From 573facd40e28f847744b41833df1129907fe6f89 Mon Sep 17 00:00:00 2001 From: 0marperez Date: Thu, 31 Oct 2024 21:39:09 -0400 Subject: [PATCH 19/20] Self review & refactoring --- .../LazilyInitializedCredentialsProvider.kt | 11 +-- .../util/AwsBusinessMetricsTestUtils.kt | 3 + .../DefaultChainCredentialsProviderTest.kt | 4 +- .../codegen/BusinessMetricsIntegration.kt | 86 ------------------- .../BusinessMetricsIntegration.kt | 32 +++++++ .../CredentialsBusinessMetricsIntegration.kt | 37 ++++++++ .../EndpointBusinessMetricsIntegration.kt | 43 ++++++++++ ...tlin.codegen.integration.KotlinIntegration | 4 +- 8 files changed, 125 insertions(+), 95 deletions(-) delete mode 100644 codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt create mode 100644 codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/businessmetrics/BusinessMetricsIntegration.kt create mode 100644 codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/businessmetrics/CredentialsBusinessMetricsIntegration.kt create mode 100644 codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/businessmetrics/EndpointBusinessMetricsIntegration.kt diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt index 675013dea77..f7edf170c52 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt @@ -22,11 +22,12 @@ public class LazilyInitializedCredentialsProvider( ) : CredentialsProvider { private val provider = lazy(initializer) - override suspend fun resolve(attributes: Attributes): Credentials { - val credentials = provider.value.resolve(attributes) - if (businessMetric == null) return credentials - return credentials.emitBusinessMetric(businessMetric) - } + override suspend fun resolve(attributes: Attributes): Credentials = + if (businessMetric == null) { + provider.value.resolve(attributes) + } else { + provider.value.resolve(attributes).emitBusinessMetric(businessMetric) + } override fun toString(): String = providerName } diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt index e3d7fc86a72..84a518946b9 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt @@ -37,5 +37,8 @@ internal fun testAttributes(vararg metrics: BusinessMetric): Attributes { internal fun Credentials.withBusinessMetrics(vararg metrics: BusinessMetric): Credentials = emitBusinessMetrics(metrics.toSet()) +/** + * Converts a [String] into an [AwsBusinessMetric.Credentials] if the identifier matches + */ internal fun String.toAwsCredentialsBusinessMetric(): BusinessMetric = AwsBusinessMetric.Credentials.entries.find { it.identifier == this } ?: throw Exception("String '$this' is not an AWS business metric") diff --git a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt index f0603fba5a0..4dd1a11d894 100644 --- a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt @@ -11,7 +11,6 @@ import aws.sdk.kotlin.runtime.util.toAwsCredentialsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.copy import aws.smithy.kotlin.runtime.httptest.TestConnection -import aws.smithy.kotlin.runtime.operation.ExecutionContext import aws.smithy.kotlin.runtime.time.Instant import aws.smithy.kotlin.runtime.util.Filesystem import aws.smithy.kotlin.runtime.util.OperatingSystem @@ -169,8 +168,7 @@ class DefaultChainCredentialsProviderTest { fun executeTest(name: String) = runTest { val test = makeTest(name) val provider = DefaultChainCredentialsProvider(platformProvider = test.testPlatform, httpClient = test.testEngine) - val attributes = ExecutionContext() - val actual = runCatching { provider.resolve(attributes) } + val actual = runCatching { provider.resolve() } val expected = test.expected when { expected is TestResult.Ok && actual.isFailure -> error("expected success, got error: $actual") diff --git a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt deleted file mode 100644 index 71882150a3e..00000000000 --- a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/BusinessMetricsIntegration.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -package aws.sdk.kotlin.codegen - -import software.amazon.smithy.kotlin.codegen.core.KotlinWriter -import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes -import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes.Auth.Signing.AwsSigningCommon.AwsSigningAttributes -import software.amazon.smithy.kotlin.codegen.core.withBlock -import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration -import software.amazon.smithy.kotlin.codegen.integration.SectionWriter -import software.amazon.smithy.kotlin.codegen.integration.SectionWriterBinding -import software.amazon.smithy.kotlin.codegen.rendering.endpoints.EndpointBusinessMetrics -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator -import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolMiddleware -import software.amazon.smithy.model.shapes.OperationShape - -/** - * Renders the addition of the [BusinessMetricsInterceptor], endpoint business metrics emitters, and a static credentials business metric. - */ -class BusinessMetricsIntegration : KotlinIntegration { - override val order: Byte - get() = super.order - - override val sectionWriters: List - get() = listOf( - SectionWriterBinding(EndpointBusinessMetrics, endpointBusinessMetricsSectionWriter), - ) - - private val endpointBusinessMetricsSectionWriter = SectionWriter { writer, _ -> - writer.write("") - writer.write( - "if (endpoint.attributes.contains(#T)) request.context.#T(#T.SERVICE_ENDPOINT_OVERRIDE)", - RuntimeTypes.Core.BusinessMetrics.ServiceEndpointOverride, - RuntimeTypes.Core.BusinessMetrics.emitBusinessMetric, - RuntimeTypes.Core.BusinessMetrics.SmithyBusinessMetric, - ) - writer.write( - "if (endpoint.attributes.contains(#T)) request.context.#T(#T.ACCOUNT_ID_BASED_ENDPOINT)", - RuntimeTypes.Core.BusinessMetrics.AccountIdBasedEndpointAccountId, - RuntimeTypes.Core.BusinessMetrics.emitBusinessMetric, - RuntimeTypes.Core.BusinessMetrics.SmithyBusinessMetric, - ) - writer.write( - "if (endpoint.attributes.contains(#T.SigningService) && endpoint.attributes[#T.SigningService] == \"s3express\") request.context.#T(#T.S3_EXPRESS_BUCKET)", - AwsSigningAttributes, - AwsSigningAttributes, - RuntimeTypes.Core.BusinessMetrics.emitBusinessMetric, - AwsRuntimeTypes.Http.Interceptors.BusinessMetrics.AwsBusinessMetric, - ) - writer.write("") - } - - override fun customizeMiddleware( - ctx: ProtocolGenerator.GenerationContext, - resolved: List, - ): List = resolved + userAgentBusinessMetricsMiddleware + credentialsBusinessMetricsMiddleware - - private val userAgentBusinessMetricsMiddleware = object : ProtocolMiddleware { - override val name: String = "UserAgentBusinessMetrics" - override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) { - writer.write( - "op.interceptors.add(#T())", - AwsRuntimeTypes.Http.Interceptors.BusinessMetrics.BusinessMetricsInterceptor, - ) - } - } - - private val credentialsBusinessMetricsMiddleware = object : ProtocolMiddleware { - override val name: String = "credentialsOverrideBusinessMetricsMiddleware" - override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) { - writer.withBlock( - "if (config.credentialsProvider is #T) {", - "}", - AwsRuntimeTypes.Config.Credentials.StaticCredentialsProvider, - ) { - write( - "op.context.#T(#T.Credentials.CREDENTIALS_CODE)", - RuntimeTypes.Core.BusinessMetrics.emitBusinessMetric, - AwsRuntimeTypes.Http.Interceptors.BusinessMetrics.AwsBusinessMetric, - ) - } - } - } -} diff --git a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/businessmetrics/BusinessMetricsIntegration.kt b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/businessmetrics/BusinessMetricsIntegration.kt new file mode 100644 index 00000000000..2a64d1a96bd --- /dev/null +++ b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/businessmetrics/BusinessMetricsIntegration.kt @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package aws.sdk.kotlin.codegen.businessmetrics + +import aws.sdk.kotlin.codegen.AwsRuntimeTypes +import software.amazon.smithy.kotlin.codegen.core.KotlinWriter +import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration +import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator +import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolMiddleware +import software.amazon.smithy.model.shapes.OperationShape + +/** + * Renders the addition of the [aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.BusinessMetricsInterceptor] + */ +class BusinessMetricsInterceptorIntegration : KotlinIntegration { + override fun customizeMiddleware( + ctx: ProtocolGenerator.GenerationContext, + resolved: List, + ): List = resolved + userAgentBusinessMetricsMiddleware + + private val userAgentBusinessMetricsMiddleware = object : ProtocolMiddleware { + override val name: String = "UserAgentBusinessMetrics" + override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) { + writer.write( + "op.interceptors.add(#T())", + AwsRuntimeTypes.Http.Interceptors.BusinessMetrics.BusinessMetricsInterceptor, + ) + } + } +} diff --git a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/businessmetrics/CredentialsBusinessMetricsIntegration.kt b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/businessmetrics/CredentialsBusinessMetricsIntegration.kt new file mode 100644 index 00000000000..d59a029ced2 --- /dev/null +++ b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/businessmetrics/CredentialsBusinessMetricsIntegration.kt @@ -0,0 +1,37 @@ +package aws.sdk.kotlin.codegen.businessmetrics + +import aws.sdk.kotlin.codegen.AwsRuntimeTypes +import software.amazon.smithy.kotlin.codegen.core.KotlinWriter +import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes +import software.amazon.smithy.kotlin.codegen.core.withBlock +import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration +import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator +import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolMiddleware +import software.amazon.smithy.model.shapes.OperationShape + +/** + * Renders the addition of some of the credentials related business metrics. + */ +class CredentialsBusinessMetricsIntegration : KotlinIntegration { + override fun customizeMiddleware( + ctx: ProtocolGenerator.GenerationContext, + resolved: List, + ): List = resolved + credentialsBusinessMetricsMiddleware + + private val credentialsBusinessMetricsMiddleware = object : ProtocolMiddleware { + override val name: String = "credentialsOverrideBusinessMetricsMiddleware" + override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) { + writer.withBlock( + "if (config.credentialsProvider is #T) {", + "}", + AwsRuntimeTypes.Config.Credentials.StaticCredentialsProvider, + ) { + write( + "op.context.#T(#T.Credentials.CREDENTIALS_CODE)", + RuntimeTypes.Core.BusinessMetrics.emitBusinessMetric, + AwsRuntimeTypes.Http.Interceptors.BusinessMetrics.AwsBusinessMetric, + ) + } + } + } +} diff --git a/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/businessmetrics/EndpointBusinessMetricsIntegration.kt b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/businessmetrics/EndpointBusinessMetricsIntegration.kt new file mode 100644 index 00000000000..5150c8d7167 --- /dev/null +++ b/codegen/aws-sdk-codegen/src/main/kotlin/aws/sdk/kotlin/codegen/businessmetrics/EndpointBusinessMetricsIntegration.kt @@ -0,0 +1,43 @@ +package aws.sdk.kotlin.codegen.businessmetrics + +import aws.sdk.kotlin.codegen.AwsRuntimeTypes +import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes +import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes.Auth.Signing.AwsSigningCommon.AwsSigningAttributes +import software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration +import software.amazon.smithy.kotlin.codegen.integration.SectionWriter +import software.amazon.smithy.kotlin.codegen.integration.SectionWriterBinding +import software.amazon.smithy.kotlin.codegen.rendering.endpoints.EndpointBusinessMetrics + +/** + * Renders the addition of endpoint & endpoint adjacent business metrics. + */ +class EndpointBusinessMetricsIntegration : KotlinIntegration { + override val sectionWriters: List + get() = listOf( + SectionWriterBinding(EndpointBusinessMetrics, endpointBusinessMetricsSectionWriter), + ) + + private val endpointBusinessMetricsSectionWriter = SectionWriter { writer, _ -> + writer.write("") + writer.write( + "if (endpoint.attributes.contains(#T)) request.context.#T(#T.SERVICE_ENDPOINT_OVERRIDE)", + RuntimeTypes.Core.BusinessMetrics.ServiceEndpointOverride, + RuntimeTypes.Core.BusinessMetrics.emitBusinessMetric, + RuntimeTypes.Core.BusinessMetrics.SmithyBusinessMetric, + ) + writer.write( + "if (endpoint.attributes.contains(#T)) request.context.#T(#T.ACCOUNT_ID_BASED_ENDPOINT)", + RuntimeTypes.Core.BusinessMetrics.AccountIdBasedEndpointAccountId, + RuntimeTypes.Core.BusinessMetrics.emitBusinessMetric, + RuntimeTypes.Core.BusinessMetrics.SmithyBusinessMetric, + ) + writer.write( + "if (endpoint.attributes.contains(#T.SigningService) && endpoint.attributes[#T.SigningService] == \"s3express\") request.context.#T(#T.S3_EXPRESS_BUCKET)", + AwsSigningAttributes, + AwsSigningAttributes, + RuntimeTypes.Core.BusinessMetrics.emitBusinessMetric, + AwsRuntimeTypes.Http.Interceptors.BusinessMetrics.AwsBusinessMetric, + ) + writer.write("") + } +} diff --git a/codegen/aws-sdk-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration b/codegen/aws-sdk-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration index fba8df4f45e..288fad2a34a 100644 --- a/codegen/aws-sdk-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration +++ b/codegen/aws-sdk-codegen/src/main/resources/META-INF/services/software.amazon.smithy.kotlin.codegen.integration.KotlinIntegration @@ -42,5 +42,7 @@ aws.sdk.kotlin.codegen.customization.cloudfrontkeyvaluestore.BackfillSigV4ACusto aws.sdk.kotlin.codegen.customization.s3.express.SigV4S3ExpressAuthSchemeIntegration aws.sdk.kotlin.codegen.customization.s3.express.S3ExpressIntegration aws.sdk.kotlin.codegen.customization.s3.S3ExpiresIntegration -aws.sdk.kotlin.codegen.BusinessMetricsIntegration +aws.sdk.kotlin.codegen.businessmetrics.BusinessMetricsInterceptorIntegration +aws.sdk.kotlin.codegen.businessmetrics.CredentialsBusinessMetricsIntegration +aws.sdk.kotlin.codegen.businessmetrics.EndpointBusinessMetricsIntegration aws.sdk.kotlin.codegen.smoketests.SmokeTestsDenyListIntegration From 85b78d7799e64eab8cd813f917adc25d1d99deab Mon Sep 17 00:00:00 2001 From: 0marperez Date: Mon, 4 Nov 2024 13:58:51 -0500 Subject: [PATCH 20/20] PR feedback --- aws-runtime/aws-config/api/aws-config.api | 18 ++++++---- .../DefaultChainCredentialsProvider.kt | 31 ++++++++++------- .../credentials/EcsCredentialsProvider.kt | 4 +-- .../EnvironmentCredentialsProvider.kt | 4 +-- .../credentials/ImdsCredentialsProvider.kt | 4 +-- .../LazilyInitializedCredentialsProvider.kt | 33 ------------------- .../credentials/ProcessCredentialsProvider.kt | 4 +-- .../credentials/ProfileCredentialsProvider.kt | 4 +-- .../credentials/SsoCredentialsProvider.kt | 6 ++-- .../StsAssumeRoleCredentialsProvider.kt | 4 +-- .../StsWebIdentityCredentialsProvider.kt | 4 +-- .../SystemPropertyCredentialsProvider.kt | 4 +-- .../credentials/EcsCredentialsProviderTest.kt | 6 ++-- .../EnvironmentCredentialsProviderTest.kt | 8 ++--- .../ImdsCredentialsProviderTest.kt | 14 ++++---- .../ProfileCredentialsProviderTest.kt | 17 ++++++---- .../credentials/SsoCredentialsProviderTest.kt | 4 +-- .../StsWebIdentityCredentialsProviderTest.kt | 4 +-- .../util/AwsBusinessMetricsTestUtils.kt | 8 ----- .../DefaultChainCredentialsProviderTest.kt | 4 +-- .../ProcessCredentialsProviderTest.kt | 8 ++--- aws-runtime/aws-http/api/aws-http.api | 4 +-- .../AwsBusinessMetricsUtils.kt | 8 ++--- gradle/libs.versions.toml | 4 +-- 24 files changed, 92 insertions(+), 117 deletions(-) delete mode 100644 aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt diff --git a/aws-runtime/aws-config/api/aws-config.api b/aws-runtime/aws-config/api/aws-config.api index 84d6427c300..cf329e7c96e 100644 --- a/aws-runtime/aws-config/api/aws-config.api +++ b/aws-runtime/aws-config/api/aws-config.api @@ -97,13 +97,6 @@ public final class aws/sdk/kotlin/runtime/auth/credentials/InvalidSsoTokenExcept public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V } -public final class aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider : aws/smithy/kotlin/runtime/auth/awscredentials/CredentialsProvider { - public fun (Ljava/lang/String;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetric;Lkotlin/jvm/functions/Function0;)V - public synthetic fun (Ljava/lang/String;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetric;Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun resolve (Laws/smithy/kotlin/runtime/collections/Attributes;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun toString ()Ljava/lang/String; -} - public final class aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider : aws/smithy/kotlin/runtime/auth/awscredentials/CredentialsProvider { public fun (Ljava/lang/String;Laws/smithy/kotlin/runtime/util/PlatformProvider;JJ)V public synthetic fun (Ljava/lang/String;Laws/smithy/kotlin/runtime/util/PlatformProvider;JJILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -216,6 +209,17 @@ public final class aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredent public static synthetic fun fromEnvironment-TUY-ock$default (Laws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider$Companion;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JLaws/smithy/kotlin/runtime/util/PlatformProvider;Laws/smithy/kotlin/runtime/http/engine/HttpClientEngine;ILjava/lang/Object;)Laws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider; } +public final class aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityProvider : aws/smithy/kotlin/runtime/auth/awscredentials/CloseableCredentialsProvider { + public fun ()V + public fun (Laws/smithy/kotlin/runtime/util/PlatformProvider;Laws/smithy/kotlin/runtime/http/engine/HttpClientEngine;Ljava/lang/String;)V + public synthetic fun (Laws/smithy/kotlin/runtime/util/PlatformProvider;Laws/smithy/kotlin/runtime/http/engine/HttpClientEngine;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun close ()V + public final fun getHttpClient ()Laws/smithy/kotlin/runtime/http/engine/HttpClientEngine; + public final fun getPlatformProvider ()Laws/smithy/kotlin/runtime/util/PlatformProvider; + public final fun getRegion ()Ljava/lang/String; + public fun resolve (Laws/smithy/kotlin/runtime/collections/Attributes;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + public final class aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProvider : aws/smithy/kotlin/runtime/auth/awscredentials/CredentialsProvider { public fun ()V public fun (Lkotlin/jvm/functions/Function1;)V diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt index d1d3e41ecf6..5b849ec0a35 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProvider.kt @@ -8,6 +8,7 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.config.imds.ImdsClient import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.* import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.http.engine.DefaultHttpEngine @@ -53,17 +54,8 @@ public class DefaultChainCredentialsProvider constructor( private val chain = CredentialsProviderChain( SystemPropertyCredentialsProvider(platformProvider::getProperty), EnvironmentCredentialsProvider(platformProvider::getenv), - LazilyInitializedCredentialsProvider( - "EnvironmentStsWebIdentityCredentialsProvider", - AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN, - ) { - // STS web identity provider can be constructed from either the profile OR 100% from the environment - StsWebIdentityCredentialsProvider.fromEnvironment( - platformProvider = platformProvider, - httpClient = httpClient, - region = region, - ) - }, + // STS web identity provider can be constructed from either the profile OR 100% from the environment + StsWebIdentityProvider(platformProvider = platformProvider, httpClient = engine, region = region), ProfileCredentialsProvider(profileName = profileName, platformProvider = platformProvider, httpClient = engine, region = region), EcsCredentialsProvider(platformProvider, engine), ImdsCredentialsProvider( @@ -90,3 +82,20 @@ public class DefaultChainCredentialsProvider constructor( override fun toString(): String = this.simpleClassName + ": " + this.chain } + +/** + * Wrapper around [StsWebIdentityCredentialsProvider] that delays any exceptions until [resolve] is invoked. + * This allows it to be part of the default chain and any failures result in the chain to move onto the next provider. + */ +public class StsWebIdentityProvider( + public val platformProvider: PlatformProvider = PlatformProvider.System, + public val httpClient: HttpClientEngine? = null, + public val region: String? = null, +) : CloseableCredentialsProvider { + override suspend fun resolve(attributes: Attributes): Credentials { + val wrapped = StsWebIdentityCredentialsProvider.fromEnvironment(platformProvider = platformProvider, httpClient = httpClient, region = region) + return wrapped.resolve(attributes).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN) + } + + override fun close() { } +} diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt index 655d0a6eb06..06afaf043c5 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProvider.kt @@ -8,7 +8,7 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric -import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetric import aws.smithy.kotlin.runtime.ErrorMetadata import aws.smithy.kotlin.runtime.auth.awscredentials.* import aws.smithy.kotlin.runtime.client.endpoints.Endpoint @@ -113,7 +113,7 @@ public class EcsCredentialsProvider( logger.debug { "obtained credentials from container metadata service; expiration=${creds.expiration?.format(TimestampFormat.ISO_8601)}" } - return creds.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_HTTP) + return creds.withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_HTTP) } private suspend fun loadAuthToken(): String? { diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProvider.kt index f24122b1355..59a0114e235 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProvider.kt @@ -8,7 +8,7 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric -import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider import aws.smithy.kotlin.runtime.auth.awscredentials.simpleClassName @@ -45,7 +45,7 @@ public class EnvironmentCredentialsProvider( sessionToken = getEnv(SESSION_TOKEN), providerName = PROVIDER_NAME, accountId = getEnv(ACCOUNT_ID), - ).emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS) } override fun toString(): String = this.simpleClassName diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt index 928bd94f728..495e3d810d6 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt @@ -10,7 +10,7 @@ import aws.sdk.kotlin.runtime.config.imds.EC2MetadataError import aws.sdk.kotlin.runtime.config.imds.ImdsClient import aws.sdk.kotlin.runtime.config.imds.InstanceMetadataProvider import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric -import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.* import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.config.resolve @@ -108,7 +108,7 @@ public class ImdsCredentialsProvider( resp.sessionToken, resp.expiration, PROVIDER_NAME, - ).emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) creds.also { mu.withLock { previousCredentials = it } diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt deleted file mode 100644 index f7edf170c52..00000000000 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider.kt +++ /dev/null @@ -1,33 +0,0 @@ -package aws.sdk.kotlin.runtime.auth.credentials - -import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric -import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials -import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider -import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric -import aws.smithy.kotlin.runtime.collections.Attributes - -/** - * A [CredentialsProvider] implementation that delays the initialization of the underlying provider until - * the first call to [resolve]. This is useful when the initialization of the credentials provider is expensive - * or should be deferred until credentials are actually needed. - * - * @param providerName The name of the credentials provider that is being wrapped. Will default to "LazilyInitializedCredentialsProvider". - * @param businessMetric The provider's business metric to emit when credentials are resolved. - * @param initializer A lambda function that provides the actual [CredentialsProvider] to be initialized lazily. - */ -public class LazilyInitializedCredentialsProvider( - private val providerName: String = "LazilyInitializedCredentialsProvider", - private val businessMetric: BusinessMetric? = null, - initializer: () -> CredentialsProvider, -) : CredentialsProvider { - private val provider = lazy(initializer) - - override suspend fun resolve(attributes: Attributes): Credentials = - if (businessMetric == null) { - provider.value.resolve(attributes) - } else { - provider.value.resolve(attributes).emitBusinessMetric(businessMetric) - } - - override fun toString(): String = providerName -} diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider.kt index c3943c5640b..5a1c3a3761b 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider.kt @@ -6,7 +6,7 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric -import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.* import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.serde.json.JsonDeserializer @@ -75,7 +75,7 @@ public class ProcessCredentialsProvider( resp.expiration ?: Instant.MAX_VALUE, PROVIDER_NAME, resp.accountId, - ).emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS) } else -> throw CredentialsProviderException("Credentials response was not of expected format") } diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt index ce1534947b3..81935c0be9e 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProvider.kt @@ -16,7 +16,7 @@ import aws.sdk.kotlin.runtime.config.imds.ImdsClient import aws.sdk.kotlin.runtime.config.profile.AwsConfigurationSource import aws.sdk.kotlin.runtime.config.profile.loadAwsSharedConfig import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric -import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetrics +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetrics import aws.sdk.kotlin.runtime.region.resolveRegion import aws.smithy.kotlin.runtime.auth.awscredentials.* import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric @@ -148,7 +148,7 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( } logger.debug { "Obtained credentials from profile; expiration=${creds.expiration?.format(TimestampFormat.ISO_8601)}" } - return creds.emitBusinessMetrics(credentialsBusinessMetrics) + return creds.withBusinessMetrics(credentialsBusinessMetrics) } override fun close() { diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProvider.kt index 43baacd2432..3cb4304b5f8 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProvider.kt @@ -9,7 +9,7 @@ import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.sso.SsoClient import aws.sdk.kotlin.runtime.auth.credentials.internal.sso.getRoleCredentials import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric -import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.* import aws.smithy.kotlin.runtime.client.SdkClientOption import aws.smithy.kotlin.runtime.collections.Attributes @@ -129,9 +129,9 @@ public class SsoCredentialsProvider public constructor( ) return if (ssoTokenProvider != null) { - creds.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_SSO) + creds.withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_SSO) } else { - creds.emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_SSO_LEGACY) + creds.withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_SSO_LEGACY) } } diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProvider.kt index d51987c3b65..e84781e95ea 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsAssumeRoleCredentialsProvider.kt @@ -14,7 +14,7 @@ import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.model.RegionDisabled import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.model.Tag import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric -import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.* import aws.smithy.kotlin.runtime.client.SdkClientOption import aws.smithy.kotlin.runtime.collections.Attributes @@ -148,7 +148,7 @@ public class StsAssumeRoleCredentialsProvider( expiration = roleCredentials.expiration, providerName = PROVIDER_NAME, accountId = accountId, - ).emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE) } override fun toString(): String = this.simpleClassName diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt index 6ef63877a24..3f5147f1968 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProvider.kt @@ -12,7 +12,7 @@ import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.assumeRoleWithWebIde import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.model.PolicyDescriptorType import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric -import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.* import aws.smithy.kotlin.runtime.client.SdkClientOption import aws.smithy.kotlin.runtime.collections.Attributes @@ -152,7 +152,7 @@ public class StsWebIdentityCredentialsProvider( expiration = roleCredentials.expiration, providerName = PROVIDER_NAME, accountId = accountId, - ).emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE_WEB_ID) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE_WEB_ID) } override fun toString(): String = this.simpleClassName diff --git a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProvider.kt b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProvider.kt index 7f0eb981590..db610022604 100644 --- a/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProvider.kt +++ b/aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/SystemPropertyCredentialsProvider.kt @@ -8,7 +8,7 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric -import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider import aws.smithy.kotlin.runtime.auth.awscredentials.simpleClassName @@ -45,7 +45,7 @@ public class SystemPropertyCredentialsProvider( sessionToken = getProperty(SESSION_TOKEN), providerName = PROVIDER_NAME, accountId = getProperty(ACCOUNT_ID), - ).emitBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_JVM_SYSTEM_PROPERTIES) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_JVM_SYSTEM_PROPERTIES) } override fun toString(): String = this.simpleClassName diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProviderTest.kt index 01bd10f5cf4..8401b507b59 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/EcsCredentialsProviderTest.kt @@ -8,7 +8,7 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric -import aws.sdk.kotlin.runtime.util.withBusinessMetrics +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException import aws.smithy.kotlin.runtime.http.Headers @@ -48,7 +48,7 @@ class EcsCredentialsProviderTest { "test-token", expectedExpiration, "EcsContainer", - ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_HTTP) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_HTTP) private fun ecsResponse(accountId: String? = null): HttpResponse { val payload = buildJsonObject { @@ -578,7 +578,7 @@ class EcsCredentialsProviderTest { expectedExpiration, "EcsContainer", "12345", - ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_HTTP) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_HTTP) assertEquals(expected, actual) engine.assertRequests() } diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProviderTest.kt index 9cacab24d7b..23605206185 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/EnvironmentCredentialsProviderTest.kt @@ -8,7 +8,7 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.client.AwsClientOption import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric -import aws.sdk.kotlin.runtime.util.withBusinessMetrics +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.collections.attributesOf import io.kotest.matchers.string.shouldContain @@ -34,7 +34,7 @@ class EnvironmentCredentialsProviderTest { "def", "ghi", providerName = "Environment", - ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS), + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS), ) } @@ -51,7 +51,7 @@ class EnvironmentCredentialsProviderTest { "def", null, providerName = "Environment", - ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS), + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS), ) } @@ -103,7 +103,7 @@ class EnvironmentCredentialsProviderTest { "def", providerName = "Environment", attributes = attributesOf { AwsClientOption.AccountId to "12345" }, - ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS) assertEquals(expected, actual) } } diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProviderTest.kt index fd9aad29e50..90a30e9e9c8 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProviderTest.kt @@ -8,7 +8,7 @@ import aws.sdk.kotlin.runtime.config.AwsSdkSetting import aws.sdk.kotlin.runtime.config.imds.* import aws.sdk.kotlin.runtime.config.imds.DEFAULT_TOKEN_TTL_SECONDS import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric -import aws.sdk.kotlin.runtime.util.withBusinessMetrics +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException import aws.smithy.kotlin.runtime.http.* @@ -128,7 +128,7 @@ class ImdsCredentialsProviderTest { "IQote///test0", expiration0, "IMDSv2", - ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) assertEquals(expected0, actual0) testClock.advance(1.seconds) @@ -140,7 +140,7 @@ class ImdsCredentialsProviderTest { "IQote///test1", expiration1, "IMDSv2", - ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) assertEquals(expected1, actual1) connection.assertRequests() @@ -197,7 +197,7 @@ class ImdsCredentialsProviderTest { "IQote///test", expiration, "IMDSv2", - ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) assertEquals(expected, actual) connection.assertRequests() @@ -336,7 +336,7 @@ class ImdsCredentialsProviderTest { sessionToken = "IQote///test", expiration = Instant.fromEpochSeconds(1631935916), providerName = "IMDSv2", - ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) assertEquals(expected, actual) @@ -404,7 +404,7 @@ class ImdsCredentialsProviderTest { sessionToken = "IQote///test", expiration = Instant.fromEpochSeconds(1631935916), providerName = "IMDSv2", - ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) val provider = ImdsCredentialsProvider( profileOverride = "imds-test-role", @@ -521,7 +521,7 @@ class ImdsCredentialsProviderTest { sessionToken = "IQote///test", expiration = Instant.fromEpochSeconds(1631935916), providerName = "IMDSv2", - ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_IMDS) val provider = ImdsCredentialsProvider( profileOverride = "imds-test-role", diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt index 23c55791472..d4f6eaf51c4 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/ProfileCredentialsProviderTest.kt @@ -8,8 +8,9 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.client.AwsClientOption import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetric +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetrics import aws.sdk.kotlin.runtime.util.testAttributes -import aws.sdk.kotlin.runtime.util.withBusinessMetrics import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.copy import aws.smithy.kotlin.runtime.collections.attributesOf @@ -45,7 +46,7 @@ class ProfileCredentialsProviderTest { ) val actual = provider.resolve() val expected = Credentials("AKID-Default", "Default-Secret") - .withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) + .withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) assertEquals(expected, actual) } @@ -74,7 +75,7 @@ class ProfileCredentialsProviderTest { ) val actual = provider.resolve() val expected = Credentials("AKID-Profile", "Profile-Secret") - .withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) + .withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) assertEquals(expected, actual) } @@ -105,7 +106,7 @@ class ProfileCredentialsProviderTest { ) val actual = provider.resolve() val expected = Credentials("AKID-Profile", "Profile-Secret") - .withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) + .withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) assertEquals(expected, actual) } @@ -334,7 +335,7 @@ class ProfileCredentialsProviderTest { ) val actual = provider.resolve() val expected = credentials("AKID-Default", "Default-Secret", accountId = "12345") - .withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) + .withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE) assertEquals(expected, actual) } @@ -421,8 +422,10 @@ class ProfileCredentialsProviderTest { Instant.fromIso8601("2019-05-29T00:21:43Z"), "Process", ).withBusinessMetrics( - AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_PROCESS, - AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS, + setOf( + AwsBusinessMetric.Credentials.CREDENTIALS_PROFILE_PROCESS, + AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS, + ), ) assertEquals(expected, actual) diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProviderTest.kt index 2c1938f7143..af0bf6acb45 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/SsoCredentialsProviderTest.kt @@ -7,7 +7,7 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric -import aws.sdk.kotlin.runtime.util.withBusinessMetrics +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetric import aws.smithy.kotlin.runtime.http.Headers import aws.smithy.kotlin.runtime.http.HttpBody import aws.smithy.kotlin.runtime.http.HttpStatusCode @@ -210,7 +210,7 @@ class SsoCredentialsProviderTest { expectedExpiration, "SSO", "123456789", - ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_SSO_LEGACY) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_SSO_LEGACY) assertEquals(expected, actual) } } diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProviderTest.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProviderTest.kt index 3a3ecc12fe1..7f4df827213 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/auth/credentials/StsWebIdentityCredentialsProviderTest.kt @@ -7,7 +7,7 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric -import aws.sdk.kotlin.runtime.util.withBusinessMetrics +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException import aws.smithy.kotlin.runtime.http.Headers import aws.smithy.kotlin.runtime.http.HttpBody @@ -36,7 +36,7 @@ private val CREDENTIALS = credentials( StsTestUtils.EPOCH + 15.minutes, "WebIdentityToken", "1234567", -).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE_WEB_ID) +).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_STS_ASSUME_ROLE_WEB_ID) class StsWebIdentityCredentialsProviderTest { // see https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html#API_AssumeRoleWithWebIdentity_ResponseElements diff --git a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt index 84a518946b9..71467475b34 100644 --- a/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt +++ b/aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/util/AwsBusinessMetricsTestUtils.kt @@ -1,8 +1,6 @@ package aws.sdk.kotlin.runtime.util import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric -import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetrics -import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.businessmetrics.BusinessMetric import aws.smithy.kotlin.runtime.businessmetrics.emitBusinessMetric import aws.smithy.kotlin.runtime.collections.Attributes @@ -31,12 +29,6 @@ internal fun testAttributes(vararg metrics: BusinessMetric): Attributes { return testAttributes } -/** - * Type alias for [emitBusinessMetrics], used for testing. - */ -internal fun Credentials.withBusinessMetrics(vararg metrics: BusinessMetric): Credentials = - emitBusinessMetrics(metrics.toSet()) - /** * Converts a [String] into an [AwsBusinessMetric.Credentials] if the identifier matches */ diff --git a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt index 4dd1a11d894..d56b63cd225 100644 --- a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/DefaultChainCredentialsProviderTest.kt @@ -6,7 +6,7 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials -import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.emitBusinessMetrics +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetrics import aws.sdk.kotlin.runtime.util.toAwsCredentialsBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.copy @@ -102,7 +102,7 @@ class DefaultChainCredentialsProviderTest { o["session_token"]?.jsonPrimitive?.content, o["expiry"]?.jsonPrimitive?.longOrNull?.let { Instant.fromEpochSeconds(it) }, accountId = o["accountId"]?.jsonPrimitive?.content, - ).emitBusinessMetrics( + ).withBusinessMetrics( expectedBusinessMetrics.map { it.toAwsCredentialsBusinessMetric() }.toSet(), ) Ok(name, docs, expectedCreds) diff --git a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProviderTest.kt b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProviderTest.kt index e8f88441286..da740a32c74 100644 --- a/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProviderTest.kt +++ b/aws-runtime/aws-config/jvm/test/aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProviderTest.kt @@ -6,7 +6,7 @@ package aws.sdk.kotlin.runtime.auth.credentials import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric -import aws.sdk.kotlin.runtime.util.withBusinessMetrics +import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetric import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException import aws.smithy.kotlin.runtime.time.Instant @@ -43,7 +43,7 @@ class ProcessCredentialsProviderTest { sessionToken = "SessionToken", expiration = Instant.fromEpochSeconds(1665705600), providerName = "Process", - ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS) val processCredentialsProvider = ProcessCredentialsProvider("anyString") val actualCredentials = processCredentialsProvider.resolve() @@ -73,7 +73,7 @@ class ProcessCredentialsProviderTest { sessionToken = "SessionToken", expiration = Instant.MAX_VALUE, providerName = "Process", - ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS) val processCredentialsProvider = ProcessCredentialsProvider("anyString") val actualCredentials = processCredentialsProvider.resolve() @@ -166,7 +166,7 @@ class ProcessCredentialsProviderTest { expiration = Instant.fromEpochSeconds(1665705600), providerName = "Process", accountId = "12345", - ).withBusinessMetrics(AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS) + ).withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_PROCESS) val processCredentialsProvider = ProcessCredentialsProvider("anyString") val actualCredentials = processCredentialsProvider.resolve() diff --git a/aws-runtime/aws-http/api/aws-http.api b/aws-runtime/aws-http/api/aws-http.api index 4e4a66587ca..8f9fe76fe31 100644 --- a/aws-runtime/aws-http/api/aws-http.api +++ b/aws-runtime/aws-http/api/aws-http.api @@ -220,8 +220,8 @@ public final class aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsB } public final class aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtilsKt { - public static final fun emitBusinessMetric (Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetric;)Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials; - public static final fun emitBusinessMetrics (Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials;Ljava/util/Set;)Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials; + public static final fun withBusinessMetric (Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetric;)Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials; + public static final fun withBusinessMetrics (Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials;Ljava/util/Set;)Laws/smithy/kotlin/runtime/auth/awscredentials/Credentials; } public final class aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/BusinessMetricsInterceptor : aws/smithy/kotlin/runtime/client/Interceptor { diff --git a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt index cfb838c074e..cf3d8586b3e 100644 --- a/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt +++ b/aws-runtime/aws-http/common/src/aws/sdk/kotlin/runtime/http/interceptors/businessmetrics/AwsBusinessMetricsUtils.kt @@ -71,7 +71,7 @@ public enum class AwsBusinessMetric(public override val identifier: String) : Bu * @param metric The [BusinessMetric] to be emitted. */ @InternalApi -public fun Credentials.emitBusinessMetric(metric: BusinessMetric): Credentials = +public fun Credentials.withBusinessMetric(metric: BusinessMetric): Credentials = when (val credentialsAttributes = this.attributes) { is MutableAttributes -> { credentialsAttributes.emitBusinessMetric(metric) @@ -89,10 +89,10 @@ public fun Credentials.emitBusinessMetric(metric: BusinessMetric): Credentials = * @param metrics The [BusinessMetric]s to be emitted. */ @InternalApi -public fun Credentials.emitBusinessMetrics(metrics: Set): Credentials { +public fun Credentials.withBusinessMetrics(metrics: Set): Credentials { var credentials = this - metrics.forEach { identifier -> - credentials = this.emitBusinessMetric(identifier) + metrics.forEach { metric -> + credentials = this.withBusinessMetric(metric) } return credentials } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2084e480fbc..76a3ee1acbe 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,8 +9,8 @@ coroutines-version = "1.9.0" atomicfu-version = "0.24.0" # smithy-kotlin codegen and runtime are versioned separately -smithy-kotlin-runtime-version = "1.3.14" -smithy-kotlin-codegen-version = "0.33.14" +smithy-kotlin-runtime-version = "1.3.17-SNAPSHOT" +smithy-kotlin-codegen-version = "0.33.17-SNAPSHOT" # codegen smithy-version = "1.50.0"