From 4171b73873c90558b33021314e2369beb1ffae9d Mon Sep 17 00:00:00 2001 From: spencergibb Date: Fri, 18 Dec 2020 16:06:41 -0500 Subject: [PATCH 1/3] Uses BindHandler if registered in bootstrap. This allows spring-cloud-context to register a TextEncryptorBindHandler and handle `{cipher}` prefixed properties prior to sending for remote data. --- ...onfigServerConfigDataLocationResolver.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigServerConfigDataLocationResolver.java b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigServerConfigDataLocationResolver.java index e3a975a7ca..5706f389b5 100644 --- a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigServerConfigDataLocationResolver.java +++ b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigServerConfigDataLocationResolver.java @@ -32,6 +32,7 @@ import org.springframework.boot.context.config.ConfigDataLocationResolverContext; import org.springframework.boot.context.config.ConfigDataResourceNotFoundException; import org.springframework.boot.context.config.Profiles; +import org.springframework.boot.context.properties.bind.BindHandler; import org.springframework.boot.context.properties.bind.Bindable; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.core.Ordered; @@ -61,15 +62,22 @@ public int getOrder() { return -1; } - protected ConfigClientProperties loadProperties(Binder binder) { + protected ConfigClientProperties loadProperties(ConfigDataLocationResolverContext context) { + Binder binder = context.getBinder(); + BindHandler bindHandler = getBindHandler(context); ConfigClientProperties configClientProperties = binder - .bind(ConfigClientProperties.PREFIX, Bindable.of(ConfigClientProperties.class)) + .bind(ConfigClientProperties.PREFIX, Bindable.of(ConfigClientProperties.class), bindHandler) .orElse(new ConfigClientProperties()); - String applicationName = binder.bind("spring.application.name", String.class).orElse("application"); + String applicationName = binder.bind("spring.application.name", Bindable.of(String.class), bindHandler) + .orElse("application"); configClientProperties.setName(applicationName); return configClientProperties; } + private BindHandler getBindHandler(ConfigDataLocationResolverContext context) { + return context.getBootstrapContext().getOrElse(BindHandler.class, null); + } + protected RestTemplate createRestTemplate(ConfigClientProperties properties) { SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); if (properties.getRequestReadTimeout() < 0) { @@ -120,8 +128,7 @@ public List resolve(ConfigDataLocationResolverCo public List resolveProfileSpecific( ConfigDataLocationResolverContext resolverContext, ConfigDataLocation location, Profiles profiles) throws ConfigDataLocationNotFoundException { - ConfigClientProperties properties = loadProperties(resolverContext.getBinder()); - + ConfigClientProperties properties = loadProperties(resolverContext); String uris = location.getNonPrefixedValue(getPrefix()); if (StringUtils.hasText(uris)) { @@ -139,7 +146,8 @@ public List resolveProfileSpecific( return createRestTemplate(props); }); - boolean discoveryEnabled = resolverContext.getBinder().bind(CONFIG_DISCOVERY_ENABLED, Boolean.class) + boolean discoveryEnabled = resolverContext.getBinder() + .bind(CONFIG_DISCOVERY_ENABLED, Bindable.of(Boolean.class), getBindHandler(resolverContext)) .orElse(false); if (discoveryEnabled) { From 66ad4bfe99e2d08ab932890a6a0f987cd4c64114 Mon Sep 17 00:00:00 2001 From: spencergibb Date: Sun, 20 Dec 2020 11:28:09 -0500 Subject: [PATCH 2/3] Adds test that a BindHandler in bootstrap context is used. --- ...onfigServerConfigDataLocationResolver.java | 2 +- ...nfigDataCustomizationIntegrationTests.java | 28 ++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigServerConfigDataLocationResolver.java b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigServerConfigDataLocationResolver.java index 5706f389b5..523d2fd66b 100644 --- a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigServerConfigDataLocationResolver.java +++ b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigServerConfigDataLocationResolver.java @@ -67,7 +67,7 @@ protected ConfigClientProperties loadProperties(ConfigDataLocationResolverContex BindHandler bindHandler = getBindHandler(context); ConfigClientProperties configClientProperties = binder .bind(ConfigClientProperties.PREFIX, Bindable.of(ConfigClientProperties.class), bindHandler) - .orElse(new ConfigClientProperties()); + .orElseGet(ConfigClientProperties::new); String applicationName = binder.bind("spring.application.name", Bindable.of(String.class), bindHandler) .orElse("application"); configClientProperties.setName(applicationName); diff --git a/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataCustomizationIntegrationTests.java b/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataCustomizationIntegrationTests.java index a993cd0f7a..15a74ea3e1 100644 --- a/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataCustomizationIntegrationTests.java +++ b/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataCustomizationIntegrationTests.java @@ -20,11 +20,17 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.BootstrapContext; +import org.springframework.boot.BootstrapRegistry; +import org.springframework.boot.Bootstrapper; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.config.ConfigData; +import org.springframework.boot.context.properties.bind.BindContext; +import org.springframework.boot.context.properties.bind.BindHandler; +import org.springframework.boot.context.properties.bind.Bindable; import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.cloud.config.client.ConfigServerBootstrapper.LoaderInterceptor; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.web.client.RestTemplate; @@ -37,7 +43,9 @@ public class ConfigServerConfigDataCustomizationIntegrationTests { void customizableRestTemplate() { ConfigurableApplicationContext context = null; try { + BindHandlerBootstrapper bindHandlerBootstrapper = new BindHandlerBootstrapper(); context = new SpringApplicationBuilder(TestConfig.class) + .addBootstrapper(bindHandlerBootstrapper) .addBootstrapper(ConfigServerBootstrapper.create().withLoaderInterceptor(new Interceptor()) .withRestTemplateFactory(this::restTemplate)) .addBootstrapper(registry -> registry.addCloseListener(event -> { @@ -47,7 +55,7 @@ void customizableRestTemplate() { RestTemplate restTemplate = bootstrapContext.get(RestTemplate.class); beanFactory.registerSingleton("holder", new RestTemplateHolder(restTemplate)); beanFactory.registerSingleton("interceptor", bootstrapContext.get(LoaderInterceptor.class)); - })).run("--spring.config.import=optional:configserver:", "--custom.prop=customval"); + })).run("--spring.config.import=optional:configserver:", "--custom.prop=customval", "--spring.cloud.config.label=mylabel"); RestTemplateHolder holder = context.getBean(RestTemplateHolder.class); assertThat(holder).isNotNull(); @@ -60,6 +68,8 @@ void customizableRestTemplate() { Interceptor interceptor = (Interceptor) loaderInterceptor; assertThat(interceptor.applied).isTrue(); assertThat(interceptor.hasBinder).isTrue(); + + assertThat(bindHandlerBootstrapper.onSuccessCount).isGreaterThan(1); } finally { if (context != null) { @@ -115,4 +125,20 @@ static class CustomRestTemplate extends RestTemplate { } + static class BindHandlerBootstrapper implements Bootstrapper { + + private int onSuccessCount = 0; + + @Override + public void intitialize(BootstrapRegistry registry) { + registry.register(BindHandler.class, context -> new BindHandler() { + @Override + public Object onSuccess(ConfigurationPropertyName name, Bindable target, BindContext context, Object result) { + onSuccessCount++; + return result; + } + }); + } + } + } From 970de637b5030bf04fd0b243e77ade4aeaf603b7 Mon Sep 17 00:00:00 2001 From: spencergibb Date: Sun, 20 Dec 2020 11:38:35 -0500 Subject: [PATCH 3/3] Makes KeyProperties a bean that is conditional on missing. --- ...gServerConfigDataCustomizationIntegrationTests.java | 10 ++++++---- .../src/test/java/sample/ApplicationFailFastTests.java | 1 - .../src/test/java/sample/ApplicationTests.java | 3 +-- .../server/config/EncryptionAutoConfiguration.java | 8 +++++++- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataCustomizationIntegrationTests.java b/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataCustomizationIntegrationTests.java index 15a74ea3e1..dc571d4fdf 100644 --- a/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataCustomizationIntegrationTests.java +++ b/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigServerConfigDataCustomizationIntegrationTests.java @@ -44,8 +44,7 @@ void customizableRestTemplate() { ConfigurableApplicationContext context = null; try { BindHandlerBootstrapper bindHandlerBootstrapper = new BindHandlerBootstrapper(); - context = new SpringApplicationBuilder(TestConfig.class) - .addBootstrapper(bindHandlerBootstrapper) + context = new SpringApplicationBuilder(TestConfig.class).addBootstrapper(bindHandlerBootstrapper) .addBootstrapper(ConfigServerBootstrapper.create().withLoaderInterceptor(new Interceptor()) .withRestTemplateFactory(this::restTemplate)) .addBootstrapper(registry -> registry.addCloseListener(event -> { @@ -55,7 +54,8 @@ void customizableRestTemplate() { RestTemplate restTemplate = bootstrapContext.get(RestTemplate.class); beanFactory.registerSingleton("holder", new RestTemplateHolder(restTemplate)); beanFactory.registerSingleton("interceptor", bootstrapContext.get(LoaderInterceptor.class)); - })).run("--spring.config.import=optional:configserver:", "--custom.prop=customval", "--spring.cloud.config.label=mylabel"); + })).run("--spring.config.import=optional:configserver:", "--custom.prop=customval", + "--spring.cloud.config.label=mylabel"); RestTemplateHolder holder = context.getBean(RestTemplateHolder.class); assertThat(holder).isNotNull(); @@ -133,12 +133,14 @@ static class BindHandlerBootstrapper implements Bootstrapper { public void intitialize(BootstrapRegistry registry) { registry.register(BindHandler.class, context -> new BindHandler() { @Override - public Object onSuccess(ConfigurationPropertyName name, Bindable target, BindContext context, Object result) { + public Object onSuccess(ConfigurationPropertyName name, Bindable target, BindContext context, + Object result) { onSuccessCount++; return result; } }); } + } } diff --git a/spring-cloud-config-sample/src/test/java/sample/ApplicationFailFastTests.java b/spring-cloud-config-sample/src/test/java/sample/ApplicationFailFastTests.java index caf5ef0398..723be8e195 100644 --- a/spring-cloud-config-sample/src/test/java/sample/ApplicationFailFastTests.java +++ b/spring-cloud-config-sample/src/test/java/sample/ApplicationFailFastTests.java @@ -25,7 +25,6 @@ public class ApplicationFailFastTests { - // FIXME: configdata failfast works. @Test public void contextFails() { try { diff --git a/spring-cloud-config-sample/src/test/java/sample/ApplicationTests.java b/spring-cloud-config-sample/src/test/java/sample/ApplicationTests.java index d44770928d..2085e21a47 100644 --- a/spring-cloud-config-sample/src/test/java/sample/ApplicationTests.java +++ b/spring-cloud-config-sample/src/test/java/sample/ApplicationTests.java @@ -40,8 +40,7 @@ @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class, // Normally spring.cloud.config.enabled:true is the default but since we have the - // config - // server on the classpath we need to set it explicitly + // config server on the classpath we need to set it explicitly properties = { "spring.cloud.config.enabled:true", // FIXME: configdata why is this needed here? "spring.config.use-legacy-processing=true", "management.security.enabled=false", diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EncryptionAutoConfiguration.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EncryptionAutoConfiguration.java index 342cec5f18..d83e202281 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EncryptionAutoConfiguration.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EncryptionAutoConfiguration.java @@ -53,10 +53,16 @@ * */ @Configuration(proxyBeanMethods = false) -@EnableConfigurationProperties(KeyProperties.class) +@EnableConfigurationProperties @Import({ SingleTextEncryptorConfiguration.class, DefaultTextEncryptorConfiguration.class }) public class EncryptionAutoConfiguration { + @Bean + @ConditionalOnMissingBean + public KeyProperties keyProperties() { + return new KeyProperties(); + } + @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(value = "spring.cloud.config.server.encrypt.enabled", matchIfMissing = true) protected static class EncryptorConfiguration {