Tighten rules around profile naming
Profiles are only allowed to use dashes, underscores, digits or letters. See gh-43176
This commit is contained in:
parent
579be1cea8
commit
0be0bed88c
|
@ -60,6 +60,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @author Scott Frederick
|
* @author Scott Frederick
|
||||||
* @author Madhura Bhave
|
* @author Madhura Bhave
|
||||||
|
* @author Sijun Yang
|
||||||
*/
|
*/
|
||||||
class SpringBootContextLoaderTests {
|
class SpringBootContextLoaderTests {
|
||||||
|
|
||||||
|
@ -127,11 +128,6 @@ class SpringBootContextLoaderTests {
|
||||||
assertThat(getActiveProfiles(MultipleActiveProfiles.class)).containsExactly("profile1", "profile2");
|
assertThat(getActiveProfiles(MultipleActiveProfiles.class)).containsExactly("profile1", "profile2");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void activeProfileWithComma() {
|
|
||||||
assertThat(getActiveProfiles(ActiveProfileWithComma.class)).containsExactly("profile1,2");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // gh-28776
|
@Test // gh-28776
|
||||||
void testPropertyValuesShouldTakePrecedenceWhenInlinedPropertiesPresent() {
|
void testPropertyValuesShouldTakePrecedenceWhenInlinedPropertiesPresent() {
|
||||||
TestContext context = new ExposedTestContextManager(SimpleConfig.class).getExposedTestContext();
|
TestContext context = new ExposedTestContextManager(SimpleConfig.class).getExposedTestContext();
|
||||||
|
@ -314,14 +310,8 @@ class SpringBootContextLoaderTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SpringBootTest(classes = Config.class)
|
|
||||||
@ActiveProfiles({ "profile1,2" })
|
|
||||||
static class ActiveProfileWithComma {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@SpringBootTest(properties = { "key=myValue" }, classes = Config.class)
|
@SpringBootTest(properties = { "key=myValue" }, classes = Config.class)
|
||||||
@ActiveProfiles({ "profile1,2" })
|
@ActiveProfiles({ "profile1" })
|
||||||
static class ActiveProfileWithInlinedProperties {
|
static class ActiveProfileWithInlinedProperties {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ import org.springframework.util.StringUtils;
|
||||||
* @author Madhura Bhave
|
* @author Madhura Bhave
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Scott Frederick
|
* @author Scott Frederick
|
||||||
|
* @author Sijun Yang
|
||||||
* @since 2.4.0
|
* @since 2.4.0
|
||||||
*/
|
*/
|
||||||
public class StandardConfigDataLocationResolver
|
public class StandardConfigDataLocationResolver
|
||||||
|
@ -154,6 +155,7 @@ public class StandardConfigDataLocationResolver
|
||||||
private Set<StandardConfigDataReference> getProfileSpecificReferences(ConfigDataLocationResolverContext context,
|
private Set<StandardConfigDataReference> getProfileSpecificReferences(ConfigDataLocationResolverContext context,
|
||||||
ConfigDataLocation[] configDataLocations, Profiles profiles) {
|
ConfigDataLocation[] configDataLocations, Profiles profiles) {
|
||||||
Set<StandardConfigDataReference> references = new LinkedHashSet<>();
|
Set<StandardConfigDataReference> references = new LinkedHashSet<>();
|
||||||
|
validateProfiles(profiles);
|
||||||
for (String profile : profiles) {
|
for (String profile : profiles) {
|
||||||
for (ConfigDataLocation configDataLocation : configDataLocations) {
|
for (ConfigDataLocation configDataLocation : configDataLocations) {
|
||||||
String resourceLocation = getResourceLocation(context, configDataLocation);
|
String resourceLocation = getResourceLocation(context, configDataLocation);
|
||||||
|
@ -163,6 +165,26 @@ public class StandardConfigDataLocationResolver
|
||||||
return references;
|
return references;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateProfiles(Profiles profiles) {
|
||||||
|
for (String profile : profiles) {
|
||||||
|
validateProfile(profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateProfile(String profile) {
|
||||||
|
Assert.hasText(profile, "Profile must contain text");
|
||||||
|
Assert.state(!profile.startsWith("-") && !profile.startsWith("_"),
|
||||||
|
() -> String.format("Invalid profile '%s': must not start with '-' or '_'", profile));
|
||||||
|
Assert.state(!profile.endsWith("-") && !profile.endsWith("_"),
|
||||||
|
() -> String.format("Invalid profile '%s': must not end with '-' or '_'", profile));
|
||||||
|
profile.codePoints().forEach((codePoint) -> {
|
||||||
|
if (codePoint == '-' || codePoint == '_' || Character.isLetterOrDigit(codePoint)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new IllegalStateException(String.format("Invalid profile '%s': must contain only letters or digits or '-' or '_'", profile));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private String getResourceLocation(ConfigDataLocationResolverContext context,
|
private String getResourceLocation(ConfigDataLocationResolverContext context,
|
||||||
ConfigDataLocation configDataLocation) {
|
ConfigDataLocation configDataLocation) {
|
||||||
String resourceLocation = configDataLocation.getNonPrefixedValue(PREFIX);
|
String resourceLocation = configDataLocation.getNonPrefixedValue(PREFIX);
|
||||||
|
|
|
@ -165,6 +165,7 @@ import static org.mockito.Mockito.spy;
|
||||||
* @author Moritz Halbritter
|
* @author Moritz Halbritter
|
||||||
* @author Tadaya Tsuyukubo
|
* @author Tadaya Tsuyukubo
|
||||||
* @author Yanming Zhou
|
* @author Yanming Zhou
|
||||||
|
* @author Sijun Yang
|
||||||
*/
|
*/
|
||||||
@ExtendWith(OutputCaptureExtension.class)
|
@ExtendWith(OutputCaptureExtension.class)
|
||||||
class SpringApplicationTests {
|
class SpringApplicationTests {
|
||||||
|
@ -252,13 +253,13 @@ class SpringApplicationTests {
|
||||||
@Test
|
@Test
|
||||||
void logsActiveProfilesWithoutProfileAndMultipleDefaults(CapturedOutput output) {
|
void logsActiveProfilesWithoutProfileAndMultipleDefaults(CapturedOutput output) {
|
||||||
MockEnvironment environment = new MockEnvironment();
|
MockEnvironment environment = new MockEnvironment();
|
||||||
environment.setDefaultProfiles("p0,p1", "default");
|
environment.setDefaultProfiles("p0", "default");
|
||||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||||
application.setWebApplicationType(WebApplicationType.NONE);
|
application.setWebApplicationType(WebApplicationType.NONE);
|
||||||
application.setEnvironment(environment);
|
application.setEnvironment(environment);
|
||||||
this.context = application.run();
|
this.context = application.run();
|
||||||
assertThat(output)
|
assertThat(output)
|
||||||
.contains("No active profile set, falling back to 2 default profiles: \"p0,p1\", \"default\"");
|
.contains("No active profile set, falling back to 2 default profiles: \"p0\", \"default\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -273,9 +274,9 @@ class SpringApplicationTests {
|
||||||
void logsActiveProfilesWithMultipleProfiles(CapturedOutput output) {
|
void logsActiveProfilesWithMultipleProfiles(CapturedOutput output) {
|
||||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||||
application.setWebApplicationType(WebApplicationType.NONE);
|
application.setWebApplicationType(WebApplicationType.NONE);
|
||||||
application.setAdditionalProfiles("p1,p2", "p3");
|
application.setAdditionalProfiles("p1", "p2");
|
||||||
application.run();
|
application.run();
|
||||||
assertThat(output).contains("The following 2 profiles are active: \"p1,p2\", \"p3\"");
|
assertThat(output).contains("The following 2 profiles are active: \"p1\", \"p2\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -44,6 +44,7 @@ import static org.mockito.Mockito.mock;
|
||||||
* @author Madhura Bhave
|
* @author Madhura Bhave
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Moritz Halbritter
|
* @author Moritz Halbritter
|
||||||
|
* @author Sijun Yang
|
||||||
*/
|
*/
|
||||||
class StandardConfigDataLocationResolverTests {
|
class StandardConfigDataLocationResolverTests {
|
||||||
|
|
||||||
|
@ -254,8 +255,8 @@ class StandardConfigDataLocationResolverTests {
|
||||||
@Test
|
@Test
|
||||||
void resolveProfileSpecificReturnsProfileSpecificFiles() {
|
void resolveProfileSpecificReturnsProfileSpecificFiles() {
|
||||||
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
|
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
|
||||||
Profiles profiles = mock(Profiles.class);
|
this.environment.setActiveProfiles("dev");
|
||||||
given(profiles.iterator()).willReturn(Collections.singletonList("dev").iterator());
|
Profiles profiles = new Profiles(this.environment, this.environmentBinder, Collections.emptyList());
|
||||||
List<StandardConfigDataResource> locations = this.resolver.resolveProfileSpecific(this.context, location,
|
List<StandardConfigDataResource> locations = this.resolver.resolveProfileSpecific(this.context, location,
|
||||||
profiles);
|
profiles);
|
||||||
assertThat(locations).hasSize(1);
|
assertThat(locations).hasSize(1);
|
||||||
|
@ -293,6 +294,84 @@ class StandardConfigDataLocationResolverTests {
|
||||||
assertThatNoException().isThrownBy(() -> this.resolver.resolve(this.context, location));
|
assertThatNoException().isThrownBy(() -> this.resolver.resolve(this.context, location));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resolveProfileSpecificWhenProfileIsValidShouldNotThrowException() {
|
||||||
|
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
|
||||||
|
this.environment.setActiveProfiles("dev-test_123");
|
||||||
|
Profiles profiles = new Profiles(this.environment, this.environmentBinder, Collections.emptyList());
|
||||||
|
assertThatNoException()
|
||||||
|
.isThrownBy(() -> this.resolver.resolveProfileSpecific(this.context, location, profiles));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resolveProfileSpecificWithNonAsciiCharactersShouldNotThrowException() {
|
||||||
|
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
|
||||||
|
this.environment.setActiveProfiles("dev-테스트_123");
|
||||||
|
Profiles profiles = new Profiles(this.environment, this.environmentBinder, Collections.emptyList());
|
||||||
|
assertThatNoException()
|
||||||
|
.isThrownBy(() -> this.resolver.resolveProfileSpecific(this.context, location, profiles));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resolveProfileSpecificWithAdditionalValidProfilesShouldNotThrowException() {
|
||||||
|
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
|
||||||
|
this.environment.setActiveProfiles("dev-test");
|
||||||
|
Profiles profiles = new Profiles(this.environment, this.environmentBinder, List.of("prod-test", "stage-test"));
|
||||||
|
assertThatNoException()
|
||||||
|
.isThrownBy(() -> this.resolver.resolveProfileSpecific(this.context, location, profiles));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resolveProfileSpecificWhenProfileStartsWithSymbolThrowsException() {
|
||||||
|
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
|
||||||
|
this.environment.setActiveProfiles("-dev");
|
||||||
|
Profiles profiles = new Profiles(this.environment, this.environmentBinder, Collections.emptyList());
|
||||||
|
assertThatIllegalStateException()
|
||||||
|
.isThrownBy(() -> this.resolver.resolveProfileSpecific(this.context, location, profiles))
|
||||||
|
.withMessageStartingWith("Invalid profile '-dev': must not start with '-' or '_'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resolveProfileSpecificWhenProfileStartsWithUnderscoreThrowsException() {
|
||||||
|
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
|
||||||
|
this.environment.setActiveProfiles("_dev");
|
||||||
|
Profiles profiles = new Profiles(this.environment, this.environmentBinder, Collections.emptyList());
|
||||||
|
assertThatIllegalStateException()
|
||||||
|
.isThrownBy(() -> this.resolver.resolveProfileSpecific(this.context, location, profiles))
|
||||||
|
.withMessageStartingWith("Invalid profile '_dev': must not start with '-' or '_'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resolveProfileSpecificWhenProfileEndsWithSymbolThrowsException() {
|
||||||
|
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
|
||||||
|
this.environment.setActiveProfiles("dev-");
|
||||||
|
Profiles profiles = new Profiles(this.environment, this.environmentBinder, Collections.emptyList());
|
||||||
|
assertThatIllegalStateException()
|
||||||
|
.isThrownBy(() -> this.resolver.resolveProfileSpecific(this.context, location, profiles))
|
||||||
|
.withMessageStartingWith("Invalid profile 'dev-': must not end with '-' or '_'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resolveProfileSpecificWhenProfileEndsWithUnderscoreThrowsException() {
|
||||||
|
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
|
||||||
|
this.environment.setActiveProfiles("dev_");
|
||||||
|
Profiles profiles = new Profiles(this.environment, this.environmentBinder, Collections.emptyList());
|
||||||
|
assertThatIllegalStateException()
|
||||||
|
.isThrownBy(() -> this.resolver.resolveProfileSpecific(this.context, location, profiles))
|
||||||
|
.withMessageStartingWith("Invalid profile 'dev_': must not end with '-' or '_'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resolveProfileSpecificWhenProfileContainsInvalidCharactersThrowsException() {
|
||||||
|
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
|
||||||
|
this.environment.setActiveProfiles("dev*test");
|
||||||
|
Profiles profiles = new Profiles(this.environment, this.environmentBinder, Collections.emptyList());
|
||||||
|
assertThatIllegalStateException()
|
||||||
|
.isThrownBy(() -> this.resolver.resolveProfileSpecific(this.context, location, profiles))
|
||||||
|
.withMessageStartingWith(
|
||||||
|
"Invalid profile 'dev*test': must contain only letters or digits or '-' or '_'");
|
||||||
|
}
|
||||||
|
|
||||||
private String filePath(String... components) {
|
private String filePath(String... components) {
|
||||||
return "file [" + String.join(File.separator, components) + "]";
|
return "file [" + String.join(File.separator, components) + "]";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue