-
Notifications
You must be signed in to change notification settings - Fork 49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
misc: credentials business metrics #1442
misc: credentials business metrics #1442
Conversation
A new generated diff is ready to view. |
A new generated diff is ready to view. |
A new generated diff is ready to view. |
@@ -7,7 +7,8 @@ | |||
"secret_access_key": "secretkeycorrect", | |||
"session_token": "tokencorrect", | |||
"expiry": 1632249686, | |||
"accountId": "130633740322" | |||
"accountId": "130633740322", | |||
"business_metrics": ["p","0", "i"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is just meant to resolve credentials from IMDS, why are there three credentials provider metrics emitted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test actually uses this config file. So p
is for the named provider, 0
is for IMDS, and i
is for the assume role call with the credentials resolved from IMDS. I think it might be a good idea to change the docs for this test because I made the same assumption when I first added the business metrics.
/** | ||
* The source used to create the [RoleArn] | ||
*/ | ||
val roleArnSource: RoleArnSource? = null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
naming: source
makes this more readable, roleArn.source
vs. roleArn.roleArnSource
@@ -318,4 +326,286 @@ class ProfileCredentialsProviderTest { | |||
val expected = credentials("AKID-Default", "Default-Secret", accountId = "12345") | |||
assertEquals(expected, actual) | |||
} | |||
|
|||
@Test | |||
fun staticCredentialsBusinessMetrics() = runTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
naming: static -> profile
override val name: String = "credentialsOverrideBusinessMetricsMiddleware" | ||
override fun render(ctx: ProtocolGenerator.GenerationContext, op: OperationShape, writer: KotlinWriter) { | ||
writer.withBlock( | ||
"if (config.credentialsProvider.#T != #S ) {", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compare classes directly rather than their simple names, if (config.credentialsProvider !is ManagedCredentialsProvider)
"ManagedCredentialsProvider", | ||
) { | ||
write( | ||
"op.context.#T(#T.Credentials.CREDENTIALS_CODE)", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This middleware will emit CREDENTIALS_CODE
any time a custom credentials provider is configured and used. I thought that this metric referred to using the StaticCredentialsProvider
.
If someone configures a custom ProfileCredentialsProvider
, should we really not be emitting the CREDENTIALS_PROFILE
metric?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CREDENTIALS_CODE
is meant for when there's any code to setup credentials, so configuring a custom / non-default credentials provider, the StaticCredentialsProvider
, session objects for boto, etc. We don't have a business metric for StaticCredentialsProvider
but it falls under CREDENTIALS_CODE
. We could get more specific in the future though, we would just have to discuss adding a metric for something like the StaticCredentialsProvider
that all SDKs share.
CREDENTIALS_PROFILE
is confusing, it's meant for static credentials in a profile. It was called something else but I think the name changed during review. Same as StaticCredentialsProvider
, we don't have a metric for ProfileCredentialsProvider
. The focus for the initial metrics was more on the default credentials provider chain related metrics not all credentials providers but we can go back and add more metrics soon because I think they would be very useful.
We still capture most of what's going on, e.g. If a user sets the ProfileCredentialsProvider
we would see that they set a non-default credentials provider, and then we could see where in the profile the credentials are resolved from because every profile option is captured in the metrics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CREDENTIALS_CODE
is now mapped to StaticCredentialsProvider
. It's not in the provider itself because it's an implementation detail for other providers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CREDENTIALS_CODE
is now mapped toStaticCredentialsProvider
. It's not in the provider itself because it's an implementation detail for other providers.
I don't think I understand the point being made here. Why can't we move this specialized logic into StaticCredentialsProvider
? This custom middleware and the symbol references seem way more complex than just tracking the metric in static code the same way we do for all other providers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue is that it's used in the ProfileCredentialsProvider. When testing I saw the StaticCredentialsProvider
business metrics being emitted when using ProfileCredentialsProvider
.
/** | ||
* Indicates if the class was created using [fromEnvironment] | ||
*/ | ||
private var createdFromEnvironment: Boolean = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand why you needed to add this but it seems a little hacky.
We only call StsWebIdentityCredentialsProvider.fromEnvironment
in one spot, the default chain, can the metrics emission for that be lifted to that point and then we can remove this createdFromEnvironment
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I originally had it in the default credentials provider chain but it wasn't working correctly so I moved it here. The default credentials provider chain doesn't have attributes so I was creating some and then merging them with the ones from the resolve call, but something about that wasn't working correctly. I can try again for a bit and see if I can make it work.
…der is configured by user
…f StsWebIdentityCredentialsProvider
A new generated diff is ready to view. |
A new generated diff is ready to view. |
A new generated diff is ready to view. |
3f082bb
to
6a7b812
Compare
A new generated diff is ready to view. |
…lly for some reason 0_o
A new generated diff is ready to view. |
/** | ||
* 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 = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: "Emit" feels like the wrong word here. If the credentials are never used then we won't have "emitted" a metric in the sense that it was included in the UA. Perhaps Credentials.withBusinessMetric
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment would go for every other use/variation of emitBusinessMetric
. It's what we've been using so far.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with Ian, it's a "nit" comment but would improve readability
public final class aws/sdk/kotlin/runtime/auth/credentials/LazilyInitializedCredentialsProvider : aws/smithy/kotlin/runtime/auth/awscredentials/CredentialsProvider { | ||
public fun <init> (Ljava/lang/String;Lkotlin/jvm/functions/Function0;)V | ||
public synthetic fun <init> (Ljava/lang/String;Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V | ||
public fun <init> (Ljava/lang/String;Laws/smithy/kotlin/runtime/businessmetrics/BusinessMetric;Lkotlin/jvm/functions/Function0;)V | ||
public synthetic fun <init> (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; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correctness: Backwards-incompatible changes
LazilyInitializedCredentialsProvider("EnvironmentStsWebIdentityCredentialsProvider") { | ||
LazilyInitializedCredentialsProvider( | ||
"EnvironmentStsWebIdentityCredentialsProvider", | ||
AwsBusinessMetric.Credentials.CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN, | ||
) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: Why should the LazilyInitializedCredentialsProvider
care about its underlying delegate's metric? Can't StsWebIdentityCredentialsProvider
record the metric itself?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was originally what I had, you can see it here. There's some conversation you can follow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't really address my initial concern of the business metrics seeming to be hacked into the credentials provider implementation. I agree with Ian, LazilyInitializedCredentialsProvider
should not have to know about business metrics
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correctness: More backward-incompatible changes.
"ManagedCredentialsProvider", | ||
) { | ||
write( | ||
"op.context.#T(#T.Credentials.CREDENTIALS_CODE)", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CREDENTIALS_CODE
is now mapped toStaticCredentialsProvider
. It's not in the provider itself because it's an implementation detail for other providers.
I don't think I understand the point being made here. Why can't we move this specialized logic into StaticCredentialsProvider
? This custom middleware and the symbol references seem way more complex than just tracking the metric in static code the same way we do for all other providers.
A new generated diff is ready to view. |
A new generated diff is ready to view. |
A new generated diff is ready to view. |
A new generated diff is ready to view. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall it looks good and getting close to merge. My last concern is around LazilyInitializedCredentialsProvider
being too closely coupled with business metrics
@@ -86,6 +88,7 @@ public class ProfileCredentialsProvider @InternalSdkApi constructor( | |||
public val httpClient: HttpClientEngine? = null, | |||
public val configurationSource: AwsConfigurationSource? = null, | |||
) : CloseableCredentialsProvider { | |||
private val credentialsBusinessMetrics: MutableSet<BusinessMetric> = mutableSetOf() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
correctness: MutableSet
does not guarantee order of elements
A generic unordered collection of elements that does not support duplicate elements, and supports adding and removing elements.
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-set/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It actually does
From MutableSet
:
LinkedHashSet
The implementation of the MutableSet interface, backed by a InternalMap implementation.
From LinkedHashSet
:
The implementation of the MutableSet interface, backed by a InternalMap implementation.
The insertion order is preserved by the corresponding InternalMap implementation.
You can double check we're using LinkedHashSet
by getting the ::class.simpleName
of the business metrics
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's the current implementation, but it's not guaranteed and could change in a future version of Kotlin. It's unlikely, and I'm open to keeping this MutableSet
but just wanted to call it out 🤷
@InternalApi | ||
public fun Credentials.emitBusinessMetrics(metrics: Set<BusinessMetric>): Credentials { | ||
var credentials = this | ||
metrics.forEach { identifier -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this is no longer an identifier but the metric itself
creds = assumeProvider.resolve(attributes) | ||
} | ||
|
||
logger.debug { "Obtained credentials from profile; expiration=${creds.expiration?.format(TimestampFormat.ISO_8601)}" } | ||
return creds | ||
return creds.emitBusinessMetrics(credentialsBusinessMetrics) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: We're calling emitBusinessMetrics
on a creds
which might already have business metrics applied, which would result in duplication if you weren't using a MutableSet
(which you might not want to be, based on my other comment above)
Quality Gate passedIssues Measures |
A new generated diff is ready to view. |
Issue #
Description of changes
-Emitting credentials related business metrics
-Tests for credentials related business metrics
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.