diff --git a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigClientProperties.java b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigClientProperties.java index 5324e526..fa579d56 100644 --- a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigClientProperties.java +++ b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigClientProperties.java @@ -48,6 +48,13 @@ public class ConfigClientProperties { */ public static final String PREFIX = "spring.cloud.config"; + /** + * Placeholder string that allows ${spring.cloud.config.name} to override + * ${spring.application.name:application}. + */ + public static final String NAME_PLACEHOLDER = "${" + ConfigClientProperties.PREFIX + + ".name:${spring.application.name:application}}"; + /** * Name of config discovery enabled property. */ @@ -374,8 +381,7 @@ public class ConfigClientProperties { public ConfigClientProperties override(org.springframework.core.env.Environment environment) { ConfigClientProperties override = new ConfigClientProperties(); BeanUtils.copyProperties(this, override); - override.setName(environment.resolvePlaceholders( - "${" + ConfigClientProperties.PREFIX + ".name:${spring.application.name:application}}")); + override.setName(environment.resolvePlaceholders(NAME_PLACEHOLDER)); if (environment.containsProperty(ConfigClientProperties.PREFIX + ".profile")) { override.setProfile(environment.getProperty(ConfigClientProperties.PREFIX + ".profile")); } diff --git a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigServicePropertySourceLocator.java b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigServicePropertySourceLocator.java index 03b1c71f..73aeda32 100644 --- a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigServicePropertySourceLocator.java +++ b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/ConfigServicePropertySourceLocator.java @@ -33,6 +33,7 @@ import org.springframework.boot.origin.OriginTrackedValue; import org.springframework.cloud.bootstrap.config.PropertySourceLocator; import org.springframework.cloud.bootstrap.support.OriginTrackedCompositePropertySource; import org.springframework.cloud.config.client.ConfigClientProperties.Credentials; +import org.springframework.cloud.config.client.validation.InvalidApplicationNameException; import org.springframework.cloud.config.environment.Environment; import org.springframework.cloud.config.environment.PropertySource; import org.springframework.core.annotation.Order; @@ -52,6 +53,7 @@ import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestTemplate; +import static org.springframework.cloud.config.client.ConfigClientProperties.NAME_PLACEHOLDER; import static org.springframework.cloud.config.client.ConfigClientProperties.STATE_HEADER; import static org.springframework.cloud.config.client.ConfigClientProperties.TOKEN_HEADER; @@ -77,6 +79,20 @@ public class ConfigServicePropertySourceLocator implements PropertySourceLocator @Retryable(interceptor = "configServerRetryInterceptor") public org.springframework.core.env.PropertySource locate(org.springframework.core.env.Environment environment) { ConfigClientProperties properties = this.defaultProperties.override(environment); + + if (StringUtils.startsWithIgnoreCase(properties.getName(), "application-")) { + InvalidApplicationNameException exception = new InvalidApplicationNameException( + properties.getName()); + if (properties.isFailFast()) { + throw exception; + } + else { + logger.warn(NAME_PLACEHOLDER + " resolved to " + properties.getName() + + ", not going to load remote properties. Ensure application name doesn't start with 'application-'"); + return null; + } + } + CompositePropertySource composite = new OriginTrackedCompositePropertySource("configService"); ConfigClientRequestTemplateFactory requestTemplateFactory = new ConfigClientRequestTemplateFactory(logger, properties); diff --git a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/diagnostics/analyzer/InvalidApplicationNameExceptionFailureAnalyzer.java b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/diagnostics/analyzer/InvalidApplicationNameExceptionFailureAnalyzer.java new file mode 100644 index 00000000..0ca31fa8 --- /dev/null +++ b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/diagnostics/analyzer/InvalidApplicationNameExceptionFailureAnalyzer.java @@ -0,0 +1,45 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.config.client.diagnostics.analyzer; + +import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; +import org.springframework.boot.diagnostics.FailureAnalysis; +import org.springframework.cloud.config.client.validation.InvalidApplicationNameException; + +/** + * An {@link AbstractFailureAnalyzer} that analyzes {@link InvalidApplicationNameException + * InvalidApplicationNameException}. + * + * @author Anshul Mehra + */ +public class InvalidApplicationNameExceptionFailureAnalyzer + extends AbstractFailureAnalyzer { + + @Override + protected FailureAnalysis analyze(Throwable rootFailure, + InvalidApplicationNameException cause) { + StringBuilder description = new StringBuilder( + String.format("%s:%n", cause.getMessage())); + description.append(String.format("%n Property: %s", cause.getProperty())); + description.append(String.format("%n Value: %s", cause.getValue())); + String action = "Change ${spring.application.name} or the ${spring.cloud.config.name} " + + "override so that it does not begin with 'application-'."; + + return new FailureAnalysis(description.toString(), action, cause); + } + +} diff --git a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/validation/InvalidApplicationNameException.java b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/validation/InvalidApplicationNameException.java new file mode 100644 index 00000000..58c21aa6 --- /dev/null +++ b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/client/validation/InvalidApplicationNameException.java @@ -0,0 +1,46 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.config.client.validation; + +import static org.springframework.cloud.config.client.ConfigClientProperties.NAME_PLACEHOLDER; + +/** + * A {@code InvalidApplicationNameException} is thrown when config client detects an + * invalid application name. + * + * @author Anshul Mehra + */ +public class InvalidApplicationNameException extends RuntimeException { + + private final String property = NAME_PLACEHOLDER; + + private final String value; + + public InvalidApplicationNameException(String currentResolvedValue) { + super("Application name must not start with 'application-'"); + this.value = currentResolvedValue; + } + + public String getProperty() { + return this.property; + } + + public String getValue() { + return this.value; + } + +} diff --git a/spring-cloud-config-client/src/main/resources/META-INF/spring.factories b/spring-cloud-config-client/src/main/resources/META-INF/spring.factories index 75ee78f5..0872e36c 100644 --- a/spring-cloud-config-client/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-config-client/src/main/resources/META-INF/spring.factories @@ -24,3 +24,7 @@ org.springframework.cloud.config.client.ConfigServerConfigDataLoader # Spring Boot BootstrapRegistryInitializers org.springframework.boot.BootstrapRegistryInitializer=\ org.springframework.cloud.config.client.ConfigClientRetryBootstrapper + +# Failure analyzers +org.springframework.boot.diagnostics.FailureAnalyzer=\ +org.springframework.cloud.config.client.diagnostics.analyzer.InvalidApplicationNameExceptionFailureAnalyzer diff --git a/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigClientAutoConfigurationTests.java b/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigClientAutoConfigurationTests.java index ea7c8b16..9329d440 100644 --- a/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigClientAutoConfigurationTests.java +++ b/spring-cloud-config-client/src/test/java/org/springframework/cloud/config/client/ConfigClientAutoConfigurationTests.java @@ -16,15 +16,19 @@ package org.springframework.cloud.config.client; -import org.junit.Test; + +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.cloud.config.client.validation.InvalidApplicationNameException; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class ConfigClientAutoConfigurationTests { @@ -47,4 +51,66 @@ public class ConfigClientAutoConfigurationTests { context.close(); } + @Test + public void invalidApplicationNameOverrideWithFailFastEnabledFailsToStartup() { + SpringApplication application = new SpringApplicationBuilder( + ConfigClientAutoConfiguration.class) + .web(WebApplicationType.NONE) + .properties("spring.cloud.config.fail-fast=true", + "spring.cloud.bootstrap.enabled=true", + "spring.cloud.config.name=application-service") + .application(); + + assertThatThrownBy(application::run) + .isInstanceOf(InvalidApplicationNameException.class).extracting("value") + .isEqualTo("application-service"); + } + + @Test + public void invalidApplicationNameOverrideWithFailFastDisabledStartsUpButNoConfigServerPropertiesAreLoaded() { + SpringApplication application = new SpringApplicationBuilder( + ConfigClientAutoConfiguration.class).web(WebApplicationType.NONE) + .properties("spring.cloud.config.name=application-service", + "spring.cloud.bootstrap.enabled=true") + .application(); + + ConfigurableApplicationContext context = application.run(); + + assertThat(context.getEnvironment().getPropertySources().get("configService")) + .isNull(); + + context.close(); + } + + @Test + public void invalidApplicationNameWithFailFastEnabledFailsToStartup() { + SpringApplication application = new SpringApplicationBuilder( + ConfigClientAutoConfiguration.class) + .web(WebApplicationType.NONE) + .properties("spring.cloud.config.fail-fast=true", + "spring.cloud.bootstrap.enabled=true", + "spring.application.name=application-service") + .application(); + + assertThatThrownBy(application::run) + .isInstanceOf(InvalidApplicationNameException.class).extracting("value") + .isEqualTo("application-service"); + } + + @Test + public void invalidApplicationNameWithFailFastDisabledStartsUpButNoConfigServerPropertiesAreLoaded() { + SpringApplication application = new SpringApplicationBuilder( + ConfigClientAutoConfiguration.class).web(WebApplicationType.NONE) + .properties("spring.application.name=application-service", + "spring.cloud.bootstrap.enabled=true") + .application(); + + ConfigurableApplicationContext context = application.run(); + + assertThat(context.getEnvironment().getPropertySources().get("configService")) + .isNull(); + + context.close(); + } + }