spring-cloud-config
spring-cloud-config copied to clipboard
Custom RestTemplate is not loaded
Describe the bug Spring Boot 2.4.5 Spring Cloud 2020.0.2
I deployed Spring Cloud Config Server on Google Cloud Run. It works fine when calling it from the command line (curl) with a generated access token using gcloud auth print-identity-token
. I want to call it from a Spring Boot application with Spring Cloud Config Client. To retrieve the access token I customized the RestTemplate
. The issue is that this customization is not loaded/called even though I followed the documentation https://docs.spring.io/spring-cloud-config/docs/3.0.0/reference/html/#_security_2. I checked by adding breakpoint in the custom configuration class but the methods are not called. I get an exception which is expected since the default RestTemplate
is still in use:
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:602)
at org.springframework.cloud.config.client.ConfigServerConfigDataLoader.getRemoteEnvironment(ConfigServerConfigDataLoader.java:269)
at org.springframework.cloud.config.client.ConfigServerConfigDataLoader.doLoad(ConfigServerConfigDataLoader.java:105)
Sample
src/main/resources/application.yml
spring:
config:
import: configserver:https://xxxxxxx.run.app
src/main/resources/bootstrap.yml
spring:
application:
name: playground
src/main/resources/META-INF/spring.factories
org.springframework.cloud.bootstrap.BootstrapConfiguration=com.company.playground.CustomConfigServiceBootstrapConfiguration
The custom configuration
@Configuration
public class CustomConfigServiceBootstrapConfiguration {
@Bean
public ConfigServicePropertySourceLocator configServicePropertySourceLocator(ConfigClientProperties clientProperties) {
ConfigServicePropertySourceLocator configServicePropertySourceLocator = new ConfigServicePropertySourceLocator(clientProperties);
configServicePropertySourceLocator.setRestTemplate(restTemplate());
return configServicePropertySourceLocator;
}
public RestTemplate restTemplate() {
return new RestTemplateBuilder()
.interceptors(new GCloudInterceptor())
.build();
}
}
The interceptor using google-auth-library-oauth2-http
public class GCloudInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
credentials.refreshIfExpired();
AccessToken accessToken = credentials.getAccessToken();
request.getHeaders().add(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken.getTokenValue());
return execution.execute(request, body);
}
}
I managed to make it work but with some changes. Basically it's not possible to use the Spring Boot Config Data Import.
Set spring.cloud.bootstrap.enabled=true
and then use the uri config in bootstrap.yml
:
spring:
application:
name: playground
cloud:
config:
uri: https://xxx.run.app
and remove spring.config.import
in application.yml
Leaving the issue open because I don't know if it's a bug, or if it's the normal.
See sample here https://github.com/spring-cloud/spring-cloud-config/blob/b1e795d8d191c04d1221ab88a30a6b2f431bba7f/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataCustomizationIntegrationTests.java#L59-L60
@spencergibb It seems to me that the documentation doesn't write that if you want to use the CustomConfigServiceBootstrapConfiguration you must set the spring.cloud.bootstrap.enabled=true. It seems that Spring is going in the direction of putting everything in the application.yaml (one file) and in this case we go backward by using the old configuration.
Don't you think it should be fixed?
@spencergibb Following our conversation in the slack channel, I found this open issue which is relevant to the problem I was describing. so maybe we can use this issue instead of creating a new one.
Here is my original message in the channel:
Spring cloud config-client docs has a section about providing custom RestTemplate which is useful in same cases like when the config-server is using oauth2 for security. It uses
ConfigServicePropertySourceLocator
as example for this customisation. But if i’m not mistaken this class (ConfigServicePropertySourceLocator
) is only used in legacy bootstrap approach and the new/default way to fetch data from config-server is viaConfigServerConfigDataLoader
. I think the docs should include an example forConfigServerConfigDataLoader
too and also it should point out thatConfigServicePropertySourceLocator
would be useful only if legacy bootstrap approach is enabled.
Do we have any update on this issue? This is the exact behavior/issue we are also getting what @kvmw had reported above and quoted from slack channel. Are we expecting any update in current ConfigServerConfigDataLoader class definition to have a support for Custom RestTemplate for use cases like oauth2; with the new/default way to fetch data from config-server?
@abjimmypro The conversation was not about changing the code but only updating documentation.
You need to implement a BootstrapRegistryInitializer
to register a RestTemplate
with an auth interceptor. Here you can find a sample.
You need to register above class in spring.factories. Check this sample again.
Please note that by the time BootstrapRegistryInitializer
is called the application properties is not loaded yet. so you cannot load oauth2 configuration form properties file. You can use other approaches like environment variables ( System.getEnv(...)
) to access your configuration.
@abjimmypro The conversation was not about changing the code but only updating documentation.
You need to implement a
BootstrapRegistryInitializer
to register aRestTemplate
with an auth interceptor. Here you can find a sample. You need to register above class in spring.factories. Check this sample again.Please note that by the time
BootstrapRegistryInitializer
is called the application properties is not loaded yet. so you cannot load oauth2 configuration form properties file. You can use other approaches like environment variables (System.getEnv(...)
) to access your configuration.
Thanks @kvmw for clarification and sample.