Skip to content

Conversation

@aajtodd
Copy link
Contributor

@aajtodd aajtodd commented Jan 11, 2022

Issue #

N/A

Description of changes

  • Adds a borrow() extension that wraps an existing provider with a no-op close implementation
  • Close the client config credentialsProvider in the service client's close implementation (if it was borrowed it will do nothing, otherwise it will close it)
  • Implement Closeable for shared AwsClientConfig
  • (breaking): remove AwsClientConfig and associated constructor
  • (breaking): make credentialsProvider required for PresignConfig (otherwise it needs to be Closeable because it was instantiating a default chain provider)
  • (refactor): inline region resolution into Client.fromEnvironment() and update it's signature to use Client.Config.Builder instead of AwsClientLoadOptions
  • (refactor): thread profileName through DefaultCredentialProviderChain constructor. Make constructor with httpClientEngine override public.

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

A new generated diff is ready to view: __generated-main...__generated-fix-provider-ownership

@github-actions
Copy link

A new generated diff is ready to view: __generated-main...__generated-fix-provider-ownership

Copy link
Contributor

@ianbotsf ianbotsf left a comment

Choose a reason for hiding this comment

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

I like the general direction of this, it's nice and clean!

Comment on lines 19 to 21
* Wraps another [CredentialsProvider] with a no-op close implementation. This inserts a level of indirection for
* use cases when a provider is explicitly given to the SDK, and it's ownership should remain with the caller.
* This allows the SDK to treat resources as owned and not have to track ownership state.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: it's → its

Comment on lines 31 to 33
// provide default no-op close
override fun 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: This seems dangerous. Given the implications of forgetting to close something, it seems like there should be no default close and implementors should have to make explicit decisions about closing.

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 could do that but every generated Config implements AwsClientConfig and so we would have to generate a close() method there.

We could do that and just do config.close() instead in the generated client close implementation?

It would be nice to express a way to say that a property represents a closeable resource I guess and have the config generator automatically render it in the close implementation.

Copy link
Contributor

Choose a reason for hiding this comment

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

It would be nice to express a way to say that a property represents a closeable resource I guess and have the config generator automatically render it in the close implementation.

+1. Seems like going this way we'd be able to verify that a given codegen output cleans up resources as expected in CI

Comment on lines +98 to +106
override fun renderClose(writer: KotlinWriter) {
writer.addImport(RuntimeTypes.IO.Closeable)
writer.write("")
.openBlock("override fun close() {")
.write("client.close()")
.write("(config.credentialsProvider as? #T)?.close()", RuntimeTypes.IO.Closeable)
.closeBlock("}")
.write("")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment: Would be nice to have a new test for this.

Comment on lines 31 to 33
// provide default no-op close
override fun close() { }

Copy link
Contributor

Choose a reason for hiding this comment

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

It would be nice to express a way to say that a property represents a closeable resource I guess and have the config generator automatically render it in the close implementation.

+1. Seems like going this way we'd be able to verify that a given codegen output cleans up resources as expected in CI

@aajtodd aajtodd requested a review from ianbotsf January 19, 2022 15:33
@github-actions
Copy link

A new generated diff is ready to view: __generated-main...__generated-fix-provider-ownership

@aajtodd aajtodd requested a review from kggilmer January 19, 2022 15:33
@github-actions
Copy link

A new generated diff is ready to view: __generated-main...__generated-fix-provider-ownership

@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 4 Code Smells

No Coverage information No Coverage information
0.8% 0.8% Duplication

@github-actions
Copy link

A new generated diff is ready to view: __generated-main...__generated-fix-provider-ownership

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.

Question regarding Closable and CredentialsProvider but nothing to warrant blocking the PR

writer.write("")
.openBlock("override fun close() {")
.write("client.close()")
.write("(config.credentialsProvider as? #T)?.close()", RuntimeTypes.IO.Closeable)
Copy link
Contributor

Choose a reason for hiding this comment

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

question

I'm missing something regarding Closable and CredentialsProvider types. The latter doesn't specify a close method, but the WrappedCredentialsProvider does combine the two, and looks here that we check to see if the cp implements closable and then call it if so. Given this would it not be simpler to have CP simply implement Closable? Put another way, what value are we gaining by having only BorrowedCredentialsProvider implement Closable?

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 value are we gaining by having only BorrowedCredentialsProvider implement Closable

It isn't the only one that implements Closeable. All of the following do:

  • DefaultChainCredentialsProvider
  • EcsCredentialsProvider
  • ImdsCredentialsProvider
  • ProfileCredentialsProvider

Environment, StsWebIdentity, StsAssumeRole, and SSO providers do not implement Closeable.

The issue is that the type is just CredentialsProvider both here and in BorrowedCredentialsProvider so both have to check if the underlying implementation actually needs to be closed or not. I have gone back and forth on this and considered making CredentialsProvider implement Closeable but ultimately decided against it to be more clear about what types do and don't need to be closed when used directly by a customer. I could be persuaded the other way though if you want to make a case for it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Personally I'm not sure what's best either way, but it may be more satisfying of "We ensure accessing AWS services is performant, secure, and reliable for developers" if CredentialProvider implements Closable. I reckon this may be so because if closability varies, intuitively it seems likely that someone may forget that a particular CP needs to be closed. Or perhaps a function that takes a non closing CP is passed a requires-closing CP during a refactor but the nuance is dropped in the refactor. If all CPs must be closed (with some of them being NOP), it seems to be less likely that this kind of bug would occur. This comes at the expense of greater overall complexity of the CP type, however the value may outweigh the cost. @ianbotsf any thoughts on this?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm in favor of explicitly modelling closability. If it can/must be closed, it should be Closeable. Otherwise, I don't think we should unnecessarily attach closing semantics to the CredentialProvider interface because some of the implementations are closeable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ya I can see both sides here and I hear the concern. I'm going to leave it as is for now and lets see how it shakes out.

@aajtodd aajtodd merged commit 6da1456 into feat-cred-providers Jan 27, 2022
@aajtodd aajtodd deleted the fix-provider-ownership branch January 27, 2022 00:59
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