Skip to content

Conversation

@aajtodd
Copy link
Contributor

@aajtodd aajtodd commented Jan 5, 2022

Issue #

N/A

Description of changes

  • Re-implement the default chain in Kotlin
  • Add unit tests adapted from the Rust test suite covering e2e behavior of the default chain
    • The tests are all a directory in a particular convention:
      • fs/* is the resulting test filesystem rooted at fs
      • test-case.json is the expected result of the test (specific to default chain tests)
      • env.json is the test environment variables to set
      • http-traffic.json is the request/response pair(s) that the engine expects/responds with (there are limitations currently in what can be asserted about the requests but the tests are setup that asserting the request isn't all that important. You'll either get the correct credentials or you wont)
  • Fixed SSO response parsing discovered as part of creating the test covering sso in the default chain (response is epoch milliseconds not epoch seconds)
  • Refactored constructors to all use consistent names for the same argument types
  • Relocated the region/credential chain implementations to be in aws-config to get the right exception type(s)

Notes

  • There are still some todos/fixmes. I will follow up with additional smaller PRs to round out the credential provider overhaul. In particular we need to hash out resource management and probably thread through profile override and HttpClientEngine from shared config.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@github-actions
Copy link

github-actions bot commented Jan 5, 2022

A new generated diff is ready to view: __generated-main...__generated-feat-default-provider

@github-actions
Copy link

github-actions bot commented Jan 6, 2022

A new generated diff is ready to view: __generated-main...__generated-feat-default-provider

Copy link
Contributor

@kggilmer kggilmer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those test cases are gold. Nice work.

}

override fun close() {
providers.forEach { (it as? Closeable)?.close() }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

concern

in the case that a CredentialsProvider.close() throws an exception, we may end up with unclosed CPs. Suggest wrapping the close in a try to address.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Will fix

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What should the behavior be? Should we rethrow the first? Add all as suppressed exceptions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this?

    override fun close() {
        var closeEx: Throwable? = null
        providers.forEach {
            try {
                (it as? Closeable)?.close()
            }catch(ex: Exception) {
                if (closeEx == null) {
                    closeEx = ex
                }else {
                    closeEx!!.addSuppressed(ex)
                }
            }

            if (closeEx != null) throw closeEx!!
        }
    }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the throw happens inside the loop, won't that still lead to unclosed providers?

I think we want something like:

val exceptions = providers.mapNotNull {
    try {
        (it as? Closeable)?.close()
        null
    } catch (t: Throwable) {
        t
    }
}
if (exceptions.isNotEmpty()) {
    val t = exceptions.first()
    exceptions.drop(1).forEach(t::addSuppressed)
    throw t
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

err whoops the throw should be outside the loop. Oddly enough I had something similar to what you are suggesting locally but opted for not allocating a list. Since you suggested it though I'm assuming you'd be in favor of it anyway. Unless anyone has an issue with it I'll just take Ian's suggestion.

import kotlin.test.assertEquals
import kotlin.test.assertTrue

// TODO - refactor to make this work in common
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question

regarding making this non-jvm, I don't see a way of doing this without platform specific file system abstractions...is there another way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea would be like a kmp version of loadResource but it requires build script support. I've seen an example in the wild but yeah it's not straightforward.


// credential providers
implementation("aws.sdk.kotlin.crt:aws-crt-kotlin:$crtKotlinVersion")
implementation(project(":aws-runtime:crt-util"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Can any code be removed from crt-util now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe haven't checked. A lot of crt-util supports signing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see anything that should be removed at a glance

Comment on lines -10 to -14
internal interface CrtCredentialsProvider : CredentialsProvider {
val crtProvider: CredentialsProviderCrt

override suspend fun getCredentials(): Credentials = crtProvider.getCredentials().toSdk()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment: Seems like a lot of related code can be removed from aws-crt-kotlin as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would need to discuss the plan for this. Just because SDK doesn't use them doesn't mean crt-kotlin can't support them (it is after all supposed to be bindings for crt for the language not SDK specific necessarily)

Comment on lines 66 to 71
override fun close() {
chain.close()
if (manageEngine) {
httpClientEngine.close()
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment: Maybe this is part of the open changes to make regarding ownership but I'd expect a CachedCredentialsProvider to be Closeable and to close its underlying provider when closed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Cached provider takes ownership so that should just be fixed here probably

Comment on lines 39 to 45

private val manageEngine = httpClientEngine == null
private val httpClientEngine = httpClientEngine ?: CrtHttpEngine()

public constructor() : this(Platform)

private val chain = CredentialsProviderChain(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The secondary constructor seems like it belongs right after the primary constructor definition, not in between private vals.

@github-actions
Copy link

github-actions bot commented Jan 7, 2022

A new generated diff is ready to view: __generated-main...__generated-feat-default-provider

@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 7 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

@github-actions
Copy link

A new generated diff is ready to view: __generated-main...__generated-feat-default-provider

@aajtodd aajtodd merged commit 1e98d70 into feat-cred-providers Jan 10, 2022
@aajtodd aajtodd deleted the feat-default-provider branch January 10, 2022 15:56
aajtodd added a commit that referenced this pull request Feb 17, 2022
…ible (#469)

Refactor credential providers to remove CRT dependency and make them KMP compatible. Added SSO provider to default chain. Lots of misc cleanup and improvements.


* feat(rt): standalone sso credentials provider (#462)
* refactor(rt)!: generated sts and sts web identity credential providers (#470)
* refactor(rt)!: implement kmp ecs provider (#475)
* feat(rt)!: implement kmp profile credentials provider (#478)
* feat(rt)!: kmp default credentials provider chain (#491)
* fix: work around machine-specific Gradle bug with aws-config variants (#496)
* fix: credentials provider ownership (#498)

Co-authored-by: Ian Botsford <83236726+ianbotsf@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants