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

feat: add properties to customize universeDomain and endpoint in KMS module #3104

Merged
merged 12 commits into from
Aug 23, 2024

Conversation

mpeddada1
Copy link
Contributor

@mpeddada1 mpeddada1 commented Aug 12, 2024

This PR introduces the following changes:

  • Introduces spring.cloud.gcp.kms.universe-domain and spring.cloud.gcp.kms.endpoint properties for customers to set the universe domain and endpoints to client settings if these values aren't null. If either universe domain or endpoint are empty then we will rely on the client library to throw an IllegalArgumentException.
  • Adds unit tests in KmsAutoConfigurationTests verify that the client settings have been correctly set.
  • Adds integration tests in KmsAutoConfigurationIntegrationTests to verify behavior of KmsTemplate methods with universeDomain being set to googleapis.com and endpoint to cloudkms.googleapis.com:443. Per offline discussion with @blakeli0 and @burkedavison, from spring framework for gcp's perspective, unit testing to ensure that we have set the universe domain and endpoints correctly in the client library provides us with sufficient coverage.

End-to-end verification with test environment endpoints have been documented in a separate test plan document.

@mpeddada1 mpeddada1 marked this pull request as ready for review August 19, 2024 13:16
@mpeddada1 mpeddada1 requested a review from a team as a code owner August 19, 2024 13:16

return KeyManagementServiceClient.create(settings);
.setHeaderProvider(new UserAgentHeaderProvider(GcpKmsAutoConfiguration.class));
if (this.universeDomain != null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Shall we consider empty String as well? Or this is the common practice?

Copy link
Member

Choose a reason for hiding this comment

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

The requirements doc suggests differentiating between null and empty string, but discussion has led to some implementation variation by language.

It is not standard practice to perform null || isEmpty checking in these AutoConfiguration classes. Search: https://github.com/search?q=repo%3AGoogleCloudPlatform%2Fspring-cloud-gcp+path%3A**AutoConfiguration.java+null&type=code

Copy link
Contributor Author

@mpeddada1 mpeddada1 Aug 22, 2024

Choose a reason for hiding this comment

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

[From offline discussion]If universe-domain is empty via spring.cloud.gcp.kms.universe-domain=:

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.google.cloud.kms.v1.KeyManagementServiceClient]: Factory method 'keyManagementClient' threw exception with message: The universe domain value cannot be empty.
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:177) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:644) ~[spring-beans-6.1.10.jar:6.1.10]
	... 33 common frames omitted
Caused by: java.lang.IllegalArgumentException: The universe domain value cannot be empty.
	at com.google.api.gax.rpc.EndpointContext$Builder.determineUniverseDomain(EndpointContext.java:237) ~[gax-2.50.0.jar:2.50.0]
	at com.google.api.gax.rpc.EndpointContext$Builder.build(EndpointContext.java:316) ~[gax-2.50.0.jar:2.50.0]
	at com.google.api.gax.rpc.StubSettings.buildEndpointContext(StubSettings.java:124) ~[gax-2.50.0.jar:2.50.0]
	at com.google.api.gax.rpc.StubSettings.<init>(StubSettings.java:106) ~[gax-2.50.0.jar:2.50.0]
	at com.google.cloud.kms.v1.stub.KeyManagementServiceStubSettings.<init>(KeyManagementServiceStubSettings.java:791) ~[google-cloud-kms-2.49.0.jar:2.49.0]
	at com.google.cloud.kms.v1.stub.KeyManagementServiceStubSettings$Builder.build(KeyManagementServiceStubSettings.java:1493) ~[google-cloud-kms-2.49.0.jar:2.49.0]
	at com.google.cloud.kms.v1.stub.KeyManagementServiceStubSettings$Builder.build(KeyManagementServiceStubSettings.java:830) ~[google-cloud-kms-2.49.0.jar:2.49.0]
	at com.google.api.gax.rpc.ClientSettings.<init>(ClientSettings.java:59) ~[gax-2.50.0.jar:2.50.0]
	at com.google.cloud.kms.v1.KeyManagementServiceSettings.<init>(KeyManagementServiceSettings.java:344) ~[google-cloud-kms-2.49.0.jar:2.49.0]
	at com.google.cloud.kms.v1.KeyManagementServiceSettings$Builder.build(KeyManagementServiceSettings.java:581) ~[google-cloud-kms-2.49.0.jar:2.49.0]
	at com.google.cloud.spring.autoconfigure.kms.GcpKmsAutoConfiguration.keyManagementClient(GcpKmsAutoConfiguration.java:83) ~[spring-cloud-gcp-autoconfigure-5.5.1-SNAPSHOT.jar:5.5.1-SNAPSHOT]
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) ~[spring-beans-6.1.10.jar:6.1.10]
	... 34 common frames omitted

When endpoint is set to an empty string, the client library throws an exception:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'kmsUniverseDomain': Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'keyManagementClient' defined in class path resource [com/google/cloud/spring/autoconfigure/kms/GcpKmsAutoConfiguration.class]: Failed to instantiate [com.google.cloud.kms.v1.KeyManagementServiceClient]: Factory method 'keyManagementClient' threw exception with message: invalid endpoint, expecting "<host>:<port>"
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:795) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:237) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1357) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1194) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962) ~[spring-context-6.1.10.jar:6.1.10]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.10.jar:6.1.10]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.3.1.jar:3.3.1]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.3.1.jar:3.3.1]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.3.1.jar:3.3.1]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.3.1.jar:3.3.1]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.3.1.jar:3.3.1]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.3.1.jar:3.3.1]
	at com.example.KmsUniverseDomain.main(KmsUniverseDomain.java:21) ~[classes/:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'keyManagementClient' defined in class path resource [com/google/cloud/spring/autoconfigure/kms/GcpKmsAutoConfiguration.class]: Failed to instantiate [com.google.cloud.kms.v1.KeyManagementServiceClient]: Factory method 'keyManagementClient' threw exception with message: invalid endpoint, expecting "<host>:<port>"
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:648) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:636) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1337) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1167) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1443) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:904) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:782) ~[spring-beans-6.1.10.jar:6.1.10]
	... 19 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.google.cloud.kms.v1.KeyManagementServiceClient]: Factory method 'keyManagementClient' threw exception with message: invalid endpoint, expecting "<host>:<port>"
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:177) ~[spring-beans-6.1.10.jar:6.1.10]
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:644) ~[spring-beans-6.1.10.jar:6.1.10]
	... 33 common frames omitted
Caused by: java.lang.IllegalArgumentException: invalid endpoint, expecting "<host>:<port>"
	at com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.validateEndpoint(InstantiatingGrpcChannelProvider.java:905) ~[gax-grpc-2.51.0.jar:2.51.0]
	at com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.withEndpoint(InstantiatingGrpcChannelProvider.java:217) ~[gax-grpc-2.51.0.jar:2.51.0]
	at com.google.api.gax.rpc.ClientContext.create(ClientContext.java:234) ~[gax-2.51.0.jar:2.51.0]
	at com.google.cloud.kms.v1.stub.GrpcKeyManagementServiceStub.create(GrpcKeyManagementServiceStub.java:482) ~[google-cloud-kms-2.50.0.jar:2.50.0]
	at com.google.cloud.kms.v1.stub.KeyManagementServiceStubSettings.createStub(KeyManagementServiceStubSettings.java:688) ~[google-cloud-kms-2.50.0.jar:2.50.0]
	at com.google.cloud.kms.v1.KeyManagementServiceClient.<init>(KeyManagementServiceClient.java:783) ~[google-cloud-kms-2.50.0.jar:2.50.0]
	at com.google.cloud.kms.v1.KeyManagementServiceClient.create(KeyManagementServiceClient.java:765) ~[google-cloud-kms-2.50.0.jar:2.50.0]
	at com.google.cloud.spring.autoconfigure.kms.GcpKmsAutoConfiguration.keyManagementClient(GcpKmsAutoConfiguration.java:83) ~[spring-cloud-gcp-autoconfigure-5.5.1-SNAPSHOT.jar:5.5.1-SNAPSHOT]
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) ~[spring-beans-6.1.10.jar:6.1.10]
	... 34 common frames omitted

@@ -65,13 +70,17 @@ GcpProjectIdProvider getGcpProjectIdProvider() {
@ConditionalOnMissingBean
public KeyManagementServiceClient keyManagementClient(CredentialsProvider googleCredentials)
throws IOException {
KeyManagementServiceSettings settings =
KeyManagementServiceSettings.Builder settings =
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Rename the variable as well since the type is changed. All the references in this method also need to be updated.

Suggested change
KeyManagementServiceSettings.Builder settings =
KeyManagementServiceSettings.Builder settingsBuilder =

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, thank you.

Copy link

sonarcloud bot commented Aug 22, 2024

@mpeddada1 mpeddada1 merged commit 1de0e36 into main Aug 23, 2024
75 of 76 checks passed
@mpeddada1 mpeddada1 deleted the kms-universeDomain branch August 23, 2024 19:56
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