Skip to content
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

Use AWSKMSClientBuilder #6

Closed
wants to merge 6 commits into from

Conversation

austinmoore-
Copy link

I decided to make a new KmsMasterKeyProviderBuilder class instead of just using the builder directly inside KmsMasterKeyProvider. There are two reasons for this:

  1. The motivation behind the client builders in the AWS Java SDK was to remove the need to have/use multiple constructors to build an object, and I felt it would be valuable to carry that reasoning over to this library. Using the builder removes the need to search for the correct constructor, and is a bit more expressive.
  2. A separate builder class ensures that code previously written using the old constructors will not break with the introduction of the client builders. I noticed that clients created with the builder are immutable, so any calls to the setters in KmsMasterKeyProvider that change the state of the KMS client (e.g. KmsMasterKeyProvider#setRegion), will throw an exception, and won't change as expected. This way felt safer.

Also, feel free to share thoughts on how the new builder can be tested. I originally added an equals method to KmsMasterKeyProvider, and was going to ensure that both the builder and constructor created objects with the same properties, but because AWSKMSClient does not have equals overriden, this proved to be difficult.

* .build();
* </pre>
*/
public class KmsMasterKeyProviderBuilder {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is already missing withMetricsCollector() and withRequestHandlers(). Does it make sense to simply inherit from AwsClientBuilder so that you always get the appropriate (and new) configuration as available? That said, I'm worried that you might need to use reflection (possibly Proxies, which would be ugly) to properly plumb all current and future calls to your inner client builder.

At the very least, please add withMetricsCollector() and withRequestHandlers() and consider more future proof solutions.

pom.xml Outdated
@@ -37,7 +37,7 @@
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk</artifactId>
<version>1.10.61</version>
<version>1.11.18</version>

Choose a reason for hiding this comment

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

Upgrade & Builder change both of the part of the PR. Shouldn't the title say so?

@@ -0,0 +1,71 @@
package com.amazonaws.crypto.examples;

Choose a reason for hiding this comment

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

Examples are great however we should prefer unit test cases as they assert the functionality as part of the build and also show the usage of it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, and we should also just update existing examples to use the new API instead of adding a new bespoke example.

Choose a reason for hiding this comment

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

@bdonlan,
Since you are the contributor and know more about this project than I do.
I have commented it in other PR and asking the same question here again.
Do you think we should put examples as part of the main sdk library,
or we should either create separate maven modules or keep them under src/test?
Usually the tendency is that being it an example, developers can put less secured code and insensitive logging or other kinds of code which could be prone to security threats. e.g. example providing public methods or utils which could reveal secrets. This is the one example I can think of right now however there could be more.
just a thought.

Copy link
Contributor

Choose a reason for hiding this comment

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

Generally I think if we're checking in example code we should make sure it's production quality example code that we want people to imitate. We should also avoid pulling in new dependencies for examples in the main repository. For the more complex example you refer to, since it involves a lot of moving parts (a client component, a server in lambda, a cloudformation template...) it might be preferable to split it off to avoid confusion - or maybe have it as a separate maven subproject in a subdirectory? I'm not enough of a maven expert to know if that is a good approach here though.

Copy link

@pravsingh pravsingh Sep 19, 2017

Choose a reason for hiding this comment

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

agree with you.
I have maven expertise and will create PR so that you can review if it makes sense.

actually aws-encryption-sdk-java dir will have a pom.xml which links to poms inside 2 subdirectories. It looks something like:

aws-encryption-sdk-java\
->    pom.xml   //artifact name for this can be aws-encryption-sdk-java-parent
->    aws-encryption-sdk-java-core
        -> pom.xml, src   //this generates aws-encryption-sdk-java artifact
->    aws-encryption-sdk-java-examples
       -> pom.xml, src  //this generates aws-encryption-sdk-java-examples artifact

this way, the renaming of repo (aws-encryption-sdk-java) will not be required. And the current and future users will not be affected.

Choose a reason for hiding this comment

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

created #32

try {
kms_.setRegion(region);
} catch (UnsupportedOperationException e) {
// Ignored. Not a problem, region should already be set by the builder.

Choose a reason for hiding this comment

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

👍

return region;
} else {
final String regionName = regionProvider.getRegion();
if (regionName != null) {

Choose a reason for hiding this comment

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

instead of using US_WEST_2 as default, could we use Regions.getCurrentRegion()?

Copy link
Contributor

Choose a reason for hiding this comment

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

Where do you see us using us-west-2 as default?

Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like a fallback in the case where the SDK's default region selection logic fails, which is reasonable enough.

Choose a reason for hiding this comment

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

@SalusaSecondus,
in Regions.java inside aws-java-sdk-core:

 /**
     * The default region that new customers in the US are encouraged to use
     * when using AWS services for the first time.
     */
    public static final Regions DEFAULT_REGION = US_WEST_2;

@bdonlan agree with you. looks good to me.

@@ -83,7 +93,7 @@
private Region region_ = Region.getRegion(Regions.DEFAULT_REGION);

@Override
public void createAlias(CreateAliasRequest arg0) throws AmazonServiceException, AmazonClientException {
public CreateAliasResult createAlias(CreateAliasRequest arg0) throws AmazonServiceException, AmazonClientException {

Choose a reason for hiding this comment

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

I see changes in the contract here.

doesn't it break the existing test cases written by developers in their own applications?

Copy link
Contributor

Choose a reason for hiding this comment

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

This is not part of the public API as it's in the test directory. It's also responding to breaking SDK API changes (i.e. it's a build fix after the dependency update).

Copy link

@pravsingh pravsingh left a comment

Choose a reason for hiding this comment

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

minor improvements needed.

Copy link
Contributor

@bdonlan bdonlan left a comment

Choose a reason for hiding this comment

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

A couple of small changes are needed to leave open the possibility of future improvements to the KMS MKP. Also, this'll need to be rebased before it can be merged.

* .build();
* </pre>
*/
public class KmsMasterKeyProviderBuilder extends AwsClientBuilder<KmsMasterKeyProviderBuilder, KmsMasterKeyProvider> {
Copy link
Contributor

Choose a reason for hiding this comment

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

So, one thing I note is that the Python SDK supports using key IDs from multiple regions in a single KMS MKP. I'd prefer to align the Java behavior more closely with the Python behavior in this regard, and we need to make sure the builder API we're creating here is amenable to this.

Copy link
Author

Choose a reason for hiding this comment

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

I believe this is supported with withKeyIds.

withKeyIds does not set a region, so you could use withDefaultRegion, add multiple key IDs, and it should create a KMS MKP for the region you set in withDefaultRegion.

/**
* Sets the region to be used by the client. Overrides any previously set region.
*/
public KmsMasterKeyProviderBuilder withRegion(Region region) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd prefer to call this withDefaultRegion to leave open the possibility of supporting keys from multiple regions in the same client in future versions.

}

/**
* Sets the region using a keyId. This will override any previously set regions.
Copy link
Contributor

Choose a reason for hiding this comment

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

We should only infer the region from a key ID if the user hasn't manually configured a region, to avoid confusion. The rule is that we'd use withDefaultRegion first, followed by the implied region in withKeyId, followed by the SDK default region.

Copy link
Author

Choose a reason for hiding this comment

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

This sounds good to me. Should we throw an exception if the user tries to use withKeyId after they have already set a default region? Or just continue with the region set with withDefaultRegion?

I'm leaning towards being explicit, and throwing an exception so that there is no unexpected behavior on the user's end. But I could be swayed.

@@ -125,7 +125,12 @@ protected KmsMasterKeyProvider(final AWSKMSClient kms, final Region region, fina
kms_ = kms;
Copy link
Contributor

Choose a reason for hiding this comment

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

We should @Deprecated the old constructors and encourage folks to move to the builder API.

I'd also suggest a builder() static method on KmsMasterKeyProvider for consistency with other builders in the SDK.

@@ -0,0 +1,71 @@
package com.amazonaws.crypto.examples;
Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, and we should also just update existing examples to use the new API instead of adding a new bespoke example.

pom.xml Outdated
@@ -37,7 +37,7 @@
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk</artifactId>
Copy link
Contributor

Choose a reason for hiding this comment

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

This will probably need a rebase.

@bdonlan
Copy link
Contributor

bdonlan commented Sep 15, 2017

I should note that it would be useful to be able to reuse KMS clients, but I don't want to tie us to an API for that until we know what multi-region KMS MKPs would look like.

@bdonlan
Copy link
Contributor

bdonlan commented Sep 15, 2017

Regarding testing, we'll need to figure out how we want to run automated integration tests for this. We have some cross-language compatibility tests that operate on KMS, but I'm not sure if we've put those in a public repository just yet, and we certainly haven't set up a publicly visible test fleet using actual AWS credentials to run KMS calls. When we do that's probably the best place to put tests for client construction.

Copy link

@pravsingh pravsingh left a comment

Choose a reason for hiding this comment

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

clear from my side.

@austinmoore- austinmoore- force-pushed the client-builders branch 2 times, most recently from 8d3813c to 40f8624 Compare September 20, 2017 22:18
@austinmoore-
Copy link
Author

Since this isn't apparent by the commit messages, I've rebased this PR onto master. I've also added 2 new commits: bebe70d, and 40f8624. They're pretty simple, but should still be looked over as I haven't looked at this SDK in a while.

@bdonlan
Copy link
Contributor

bdonlan commented Nov 8, 2017

Thanks for the submission, and sorry for the radio silence - it wasn't obvious that progress was happening on this for a while and it dropped off my raday. In the meantime, I've had some work-in-progress code that did something like this kicking around for a while in my local git repo and finally put it up as PR #40. The main notable differences are that the one I submitted supports contacting multiple regions from the same MKP, and marks a bunch of older methods as now being deprecated. It also includes tests ;) I'd encourage you to check it out and see if it is along the lines of what you wanted to see from the API.

@bdonlan
Copy link
Contributor

bdonlan commented Dec 5, 2017

Closing this as moot at this point as we merged a different version of the builder concept.

@bdonlan bdonlan closed this Dec 5, 2017
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.

4 participants