From edcb153571cba0e88c67b3beb98c5237420f2a6a Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Wed, 2 Dec 2015 13:18:09 +0000 Subject: [PATCH] Support for multiple profiles in profile placeholders In a file-based repository up to now we only support single profiles in a placeholder. This change adds full support for multiple profiles in the native profile and slightly limited support for git backends (only the first profile to match an actual repository will be used to locate the physical repository, but all profiles will be applied to the search inside that repository). --- .../MultipleJGitEnvironmentRepository.java | 76 +++++--- .../NativeEnvironmentRepository.java | 39 ++-- ...pplicationPlaceholderRepositoryTests.java} | 25 ++- ...mentProfilePlaceholderRepositoryTests.java | 167 ++++++++++++++++++ .../NativeEnvironmentRepositoryTests.java | 17 ++ .../test/mysql/application.properties | 1 + 6 files changed, 285 insertions(+), 40 deletions(-) rename spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/{MultipleJGitEnvironmentUriTemplateRepositoryTests.java => MultipleJGitEnvironmentApplicationPlaceholderRepositoryTests.java} (81%) create mode 100644 spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentProfilePlaceholderRepositoryTests.java create mode 100644 spring-cloud-config-server/src/test/resources/test/mysql/application.properties diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepository.java index 3f3b3ef4..987cfcef 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepository.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentRepository.java @@ -87,17 +87,24 @@ public class MultipleJGitEnvironmentRepository extends JGitEnvironmentRepository public Locations getLocations(String application, String profile, String label) { for (PatternMatchingJGitEnvironmentRepository repository : this.repos.values()) { if (repository.matches(application, profile, label)) { - JGitEnvironmentRepository candidate = getRepository(repository, - application, profile, label); - Environment source = candidate.findOne(application, profile, label); - if (source != null) { - return repository.getLocations(application, profile, label); + for (JGitEnvironmentRepository candidate : getRepositories(repository, + application, profile, label)) { + try { + Environment source = candidate.findOne(application, profile, + label); + if (source != null) { + return candidate.getLocations(application, profile, label); + } + } + catch (Exception e) { + continue; + } } } } - JGitEnvironmentRepository candidate = getRepository(this, - application, profile, label); - if (candidate==this) { + JGitEnvironmentRepository candidate = getRepository(this, application, profile, + label); + if (candidate == this) { return super.getLocations(application, profile, label); } return candidate.getLocations(application, profile, label); @@ -107,35 +114,56 @@ public class MultipleJGitEnvironmentRepository extends JGitEnvironmentRepository public Environment findOne(String application, String profile, String label) { for (PatternMatchingJGitEnvironmentRepository repository : this.repos.values()) { if (repository.matches(application, profile, label)) { - JGitEnvironmentRepository candidate = getRepository(repository, - application, profile, label); - Environment source = candidate.findOne(application, profile, label); - if (source != null) { - return source; + for (JGitEnvironmentRepository candidate : getRepositories(repository, + application, profile, label)) { + try { + Environment source = candidate.findOne(application, profile, + label); + if (source != null) { + return source; + } + } + catch (Exception e) { + this.logger.info( + "Cannot load configuration from " + candidate.getUri()); + continue; + } } } } - JGitEnvironmentRepository candidate = getRepository(this, - application, profile, label); - if (candidate==this) { + JGitEnvironmentRepository candidate = getRepository(this, application, profile, + label); + if (candidate == this) { return super.findOne(application, profile, label); } return candidate.findOne(application, profile, label); } + private List getRepositories( + JGitEnvironmentRepository repository, String application, String profile, + String label) { + List list = new ArrayList<>(); + String[] profiles = profile == null ? new String[] { null } + : StringUtils.commaDelimitedListToStringArray(profile); + for (int i = profiles.length; i-- > 0;) { + list.add(getRepository(repository, application, profiles[i], label)); + } + return list; + } + private JGitEnvironmentRepository getRepository(JGitEnvironmentRepository repository, String application, String profile, String label) { if (!repository.getUri().contains("{")) { return repository; } String key = repository.getUri(); - if (application!=null) { + if (application != null) { key = key.replace("{application}", application); } - if (profile!=null) { + if (profile != null) { key = key.replace("{profile}", profile); } - if (label!=null) { + if (label != null) { key = key.replace("{label}", label); } if (!this.repos.containsKey(key)) { @@ -173,10 +201,12 @@ public class MultipleJGitEnvironmentRepository extends JGitEnvironmentRepository if (this.pattern == null || this.pattern.length == 0) { return false; } - - if (PatternMatchUtils.simpleMatch(this.pattern, - application + "/" + profile)) { - return true; + String[] profiles = StringUtils.commaDelimitedListToStringArray(profile); + for (int i = profiles.length; i-- > 0;) { + if (PatternMatchUtils.simpleMatch(this.pattern, + application + "/" + profiles[i])) { + return true; + } } return false; } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/NativeEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/NativeEnvironmentRepository.java index 8b31479f..2dff7fba 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/NativeEnvironmentRepository.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/NativeEnvironmentRepository.java @@ -122,21 +122,27 @@ public class NativeEnvironmentRepository } List output = new ArrayList(); for (String location : locations) { - String value = location; - if (application != null) { - value = value.replace("{application}", application); - } + String[] profiles = new String[] { profile }; if (profile != null) { - value = value.replace("{profile}", profile); + profiles = StringUtils.commaDelimitedListToStringArray(profile); } - if (label != null) { - value = value.replace("{label}", label); - } - if (!value.endsWith("/")) { - value = value + "/"; - } - if (isDirectory(value)) { - output.add(value); + for (String prof : profiles) { + String value = location; + if (application != null) { + value = value.replace("{application}", application); + } + if (prof != null) { + value = value.replace("{profile}", prof); + } + if (label != null) { + value = value.replace("{label}", label); + } + if (!value.endsWith("/")) { + value = value + "/"; + } + if (isDirectory(value)) { + output.add(value); + } } } for (String location : locations) { @@ -175,9 +181,10 @@ public class NativeEnvironmentRepository .cleanPath(new File(normal.substring("file:".length())) .getAbsolutePath()); } - String profile = result.getProfiles() == null ? null : StringUtils.arrayToCommaDelimitedString(result.getProfiles()); - for (String pattern : getLocations(result.getName(), profile, result.getLabel()) - .getLocations()) { + String profile = result.getProfiles() == null ? null + : StringUtils.arrayToCommaDelimitedString(result.getProfiles()); + for (String pattern : getLocations(result.getName(), profile, + result.getLabel()).getLocations()) { if (!pattern.contains(":")) { pattern = "file:" + pattern; } diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentUriTemplateRepositoryTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentApplicationPlaceholderRepositoryTests.java similarity index 81% rename from spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentUriTemplateRepositoryTests.java rename to spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentApplicationPlaceholderRepositoryTests.java index 840c7a5d..3a0046ea 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentUriTemplateRepositoryTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentApplicationPlaceholderRepositoryTests.java @@ -23,9 +23,11 @@ import java.util.HashMap; import java.util.Map; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.springframework.cloud.config.environment.Environment; import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentRepository.PatternMatchingJGitEnvironmentRepository; +import org.springframework.cloud.config.server.environment.SearchPathLocator.Locations; import org.springframework.cloud.config.server.test.ConfigServerTestUtils; import org.springframework.core.env.StandardEnvironment; @@ -33,7 +35,7 @@ import org.springframework.core.env.StandardEnvironment; * @author Dave Syer * */ -public class MultipleJGitEnvironmentUriTemplateRepositoryTests { +public class MultipleJGitEnvironmentApplicationPlaceholderRepositoryTests { private StandardEnvironment environment = new StandardEnvironment(); private MultipleJGitEnvironmentRepository repository = new MultipleJGitEnvironmentRepository( @@ -76,6 +78,17 @@ public class MultipleJGitEnvironmentUriTemplateRepositoryTests { assertVersion(environment); } + @Test + public void missingRepo() { + Environment environment = this.repository.findOne("missing-config-repo", + "staging", "master"); + assertEquals("Wrong property sources: " + environment, 1, + environment.getPropertySources().size()); + assertEquals(this.repository.getUri() + "/application.yml", + environment.getPropertySources().get(0).getName()); + assertVersion(environment); + } + @Test public void mappingRepo() { Environment environment = this.repository.findOne("test1-config-repo", "staging", @@ -100,6 +113,16 @@ public class MultipleJGitEnvironmentUriTemplateRepositoryTests { assertVersion(environment); } + @Test + @Ignore("not supported yet (placeholders in search paths)") + public void profilesInSearchPaths() { + this.repository.setSearchPaths("{profile}"); + Locations locations = this.repository.getLocations("foo", "dev,one,two", + "master"); + assertEquals(3, locations.getLocations().length); + assertEquals("classpath:/test/dev/", locations.getLocations()[0]); + } + private void assertVersion(Environment environment) { String version = environment.getVersion(); assertNotNull("version was null", version); diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentProfilePlaceholderRepositoryTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentProfilePlaceholderRepositoryTests.java new file mode 100644 index 00000000..732ed9f3 --- /dev/null +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/MultipleJGitEnvironmentProfilePlaceholderRepositoryTests.java @@ -0,0 +1,167 @@ +/* + * Copyright 2013-2015 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 + * + * http://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.server.environment; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.cloud.config.environment.Environment; +import org.springframework.cloud.config.server.environment.MultipleJGitEnvironmentRepository.PatternMatchingJGitEnvironmentRepository; +import org.springframework.cloud.config.server.environment.SearchPathLocator.Locations; +import org.springframework.cloud.config.server.test.ConfigServerTestUtils; +import org.springframework.core.env.StandardEnvironment; + +/** + * @author Dave Syer + * + */ +public class MultipleJGitEnvironmentProfilePlaceholderRepositoryTests { + + private StandardEnvironment environment = new StandardEnvironment(); + private MultipleJGitEnvironmentRepository repository = new MultipleJGitEnvironmentRepository( + this.environment); + + @Before + public void init() throws Exception { + String defaultUri = ConfigServerTestUtils.prepareLocalRepo("config-repo"); + this.repository.setUri(defaultUri); + this.repository.setRepos(createRepositories()); + } + + private Map createRepositories() + throws Exception { + String test1Uri = ConfigServerTestUtils.prepareLocalRepo("test1-config-repo"); + ConfigServerTestUtils.prepareLocalRepo("test2-config-repo"); + + Map repos = new HashMap<>(); + repos.put("templates", createRepository("test", "*-config-repo", + test1Uri.replace("test1-config-repo", "{profile}"))); + return repos; + } + + private PatternMatchingJGitEnvironmentRepository createRepository(String name, + String pattern, String uri) { + PatternMatchingJGitEnvironmentRepository repo = new PatternMatchingJGitEnvironmentRepository(); + repo.setEnvironment(this.environment); + repo.setName(name); + repo.setPattern(new String[] { pattern }); + repo.setUri(uri); + return repo; + } + + @Test + public void defaultRepo() { + Environment environment = this.repository.findOne("bar", "staging", "master"); + assertEquals(2, environment.getPropertySources().size()); + assertEquals(this.repository.getUri() + "/bar.properties", + environment.getPropertySources().get(0).getName()); + assertVersion(environment); + } + + @Test + public void mappingRepo() { + Environment environment = this.repository.findOne("application", + "test1-config-repo", "master"); + assertEquals(1, environment.getPropertySources().size()); + assertEquals( + getUri("*").replace("{profile}", "test1-config-repo") + + "/application.yml", + environment.getPropertySources().get(0).getName()); + assertVersion(environment); + } + + @Test + public void otherMappingRepo() { + Environment environment = this.repository.findOne("application", + "test2-config-repo", "master"); + assertEquals(1, environment.getPropertySources().size()); + assertEquals( + getUri("*").replace("{profile}", "test2-config-repo") + + "/application.properties", + environment.getPropertySources().get(0).getName()); + assertVersion(environment); + } + + @Test + public void locationsTwoProfiles() throws Exception { + Locations locations = this.repository.getLocations("application", + "test1-config-repo,test2-config-repo", "master"); + assertEquals(1, locations.getLocations().length); + assertEquals( + new File(getUri("*").replace("{profile}", "test2-config-repo") + .replace("file:", "")).getCanonicalPath(), + new File(locations.getLocations()[0].replace("file:", "")) + .getCanonicalPath()); + } + + @Test + public void locationsMissingProfile() throws Exception { + Locations locations = this.repository.getLocations("application", + "not-there,another-not-there", "master"); + assertEquals(1, locations.getLocations().length); + assertEquals( + new File(this.repository.getUri().replace("file:", "")) + .getCanonicalPath(), + new File(locations.getLocations()[0].replace("file:", "")) + .getCanonicalPath()); + } + + @Test + public void twoMappingRepos() { + Environment environment = this.repository.findOne("application", + "test1-config-repo,test2-config-repo,missing-config-repo", "master"); + assertEquals(1, environment.getPropertySources().size()); + assertEquals( + getUri("*").replace("{profile}", "test2-config-repo") + + "/application.properties", + environment.getPropertySources().get(0).getName()); + assertVersion(environment); + assertArrayEquals(environment.getProfiles(), new String[] { "test1-config-repo", + "test2-config-repo", "missing-config-repo" }); + } + + private void assertVersion(Environment environment) { + String version = environment.getVersion(); + assertNotNull("version was null", version); + assertTrue("version length was wrong", + version.length() >= 40 && version.length() <= 64); + } + + private String getUri(String pattern) { + String uri = null; + + Map repoMappings = this.repository + .getRepos(); + + for (PatternMatchingJGitEnvironmentRepository repo : repoMappings.values()) { + String[] mappingPattern = repo.getPattern(); + if (mappingPattern != null && mappingPattern.length != 0) { + uri = repo.getUri(); + break; + } + } + + return uri; + } +} diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/NativeEnvironmentRepositoryTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/NativeEnvironmentRepositoryTests.java index b35ee909..14d0e0e8 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/NativeEnvironmentRepositoryTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/NativeEnvironmentRepositoryTests.java @@ -110,6 +110,15 @@ public class NativeEnvironmentRepositoryTests { environment.getPropertySources().get(0).getSource().get("foo")); } + @Test + public void placeholdersProfiles() { + this.repository.setSearchLocations("classpath:/test/{profile}/"); + Environment environment = this.repository.findOne("foo", "dev,mysql", "master"); + assertEquals(2, environment.getPropertySources().size()); + assertEquals("mysql", + environment.getPropertySources().get(0).getSource().get("foo")); + } + @Test public void placeholdersApplicationAndProfile() { this.repository.setSearchLocations("classpath:/test/{profile}/{application}/"); @@ -127,6 +136,14 @@ public class NativeEnvironmentRepositoryTests { assertEquals("classpath:/test/foo/", locations.getLocations()[0]); } + @Test + public void locationProfilesApplication() { + this.repository.setSearchLocations("classpath:/test/{profile}"); + Locations locations = this.repository.getLocations("foo", "dev,one,two", "master"); + assertEquals(3, locations.getLocations().length); + assertEquals("classpath:/test/dev/", locations.getLocations()[0]); + } + @Test public void placeholdersNoTrailingSlash() { this.repository.setSearchLocations("classpath:/test/{label}"); diff --git a/spring-cloud-config-server/src/test/resources/test/mysql/application.properties b/spring-cloud-config-server/src/test/resources/test/mysql/application.properties new file mode 100644 index 00000000..10291126 --- /dev/null +++ b/spring-cloud-config-server/src/test/resources/test/mysql/application.properties @@ -0,0 +1 @@ +foo:mysql \ No newline at end of file