Support for placeholders in git and file URIs

The default git repository and also the native one (local files)
now support placeholders for {application}, {profile} and
{label} in the URI (searchLocation for the native repository).

Fixes gh-257
This commit is contained in:
Dave Syer
2015-12-02 11:14:45 +00:00
parent ccdbfb04e6
commit 87a1e773a0
32 changed files with 418 additions and 31 deletions

View File

@@ -16,6 +16,7 @@
package org.springframework.cloud.config.server.environment;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
@@ -23,6 +24,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.core.env.ConfigurableEnvironment;
@@ -48,6 +50,8 @@ public class MultipleJGitEnvironmentRepository extends JGitEnvironmentRepository
private Map<String, PatternMatchingJGitEnvironmentRepository> repos = new LinkedHashMap<String, PatternMatchingJGitEnvironmentRepository>();
private Map<String, JGitEnvironmentRepository> placeholders = new LinkedHashMap<String, JGitEnvironmentRepository>();
public MultipleJGitEnvironmentRepository(ConfigurableEnvironment environment) {
super(environment);
}
@@ -82,24 +86,72 @@ public class MultipleJGitEnvironmentRepository extends JGitEnvironmentRepository
@Override
public Locations getLocations(String application, String profile, String label) {
for (PatternMatchingJGitEnvironmentRepository repository : this.repos.values()) {
Environment source = repository.findOne(application, profile, label);
if (source != null) {
return repository.getLocations(application, profile, label);
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);
}
}
}
return super.getLocations(application, profile, label);
JGitEnvironmentRepository candidate = getRepository(this,
application, profile, label);
if (candidate==this) {
return super.getLocations(application, profile, label);
}
return candidate.getLocations(application, profile, label);
}
@Override
public Environment findOne(String application, String profile, String label) {
for (PatternMatchingJGitEnvironmentRepository repository : this.repos.values()) {
Environment source = repository.findOne(application, profile, label);
if (source != null) {
return source;
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;
}
}
}
JGitEnvironmentRepository candidate = getRepository(this,
application, profile, label);
if (candidate==this) {
return super.findOne(application, profile, label);
}
return candidate.findOne(application, profile, label);
}
return super.findOne(application, profile, label);
private JGitEnvironmentRepository getRepository(JGitEnvironmentRepository repository,
String application, String profile, String label) {
if (!repository.getUri().contains("{")) {
return repository;
}
String key = repository.getUri();
if (application!=null) {
key = key.replace("{application}", application);
}
if (profile!=null) {
key = key.replace("{profile}", profile);
}
if (label!=null) {
key = key.replace("{label}", label);
}
if (!this.repos.containsKey(key)) {
this.placeholders.put(key, getRepository(repository, key));
}
return this.placeholders.get(key);
}
private JGitEnvironmentRepository getRepository(JGitEnvironmentRepository source,
String uri) {
JGitEnvironmentRepository repository = new JGitEnvironmentRepository(null);
File basedir = repository.getBasedir();
BeanUtils.copyProperties(source, repository);
repository.setUri(uri);
repository.setBasedir(basedir);
return repository;
}
public static class PatternMatchingJGitEnvironmentRepository
@@ -117,6 +169,18 @@ public class MultipleJGitEnvironmentRepository extends JGitEnvironmentRepository
setUri(uri);
}
public boolean matches(String application, String profile, String label) {
if (this.pattern == null || this.pattern.length == 0) {
return false;
}
if (PatternMatchUtils.simpleMatch(this.pattern,
application + "/" + profile)) {
return true;
}
return false;
}
@Override
public Environment findOne(String application, String profile, String label) {

View File

@@ -98,7 +98,7 @@ public class NativeEnvironmentRepository
ConfigurableEnvironment environment = getEnvironment(profile);
builder.environment(environment);
builder.web(false).bannerMode(Mode.OFF);
String[] args = getArgs(config, label);
String[] args = getArgs(config, profile, label);
// Explicitly set the listeners (to exclude logging listener which would change
// log levels in the caller)
builder.application()
@@ -122,7 +122,22 @@ public class NativeEnvironmentRepository
}
List<String> output = new ArrayList<String>();
for (String location : locations) {
output.add(location);
String value = location;
if (application != null) {
value = value.replace("{application}", application);
}
if (profile != null) {
value = value.replace("{profile}", profile);
}
if (label != null) {
value = value.replace("{label}", label);
}
if (!value.endsWith("/")) {
value = value + "/";
}
if (isDirectory(value)) {
output.add(value);
}
}
for (String location : locations) {
if (isDirectory(location) && StringUtils.hasText(label)) {
@@ -160,7 +175,8 @@ public class NativeEnvironmentRepository
.cleanPath(new File(normal.substring("file:".length()))
.getAbsolutePath());
}
for (String pattern : getLocations(null, null, result.getLabel())
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;
@@ -195,8 +211,9 @@ public class NativeEnvironmentRepository
return result;
}
private String[] getArgs(String config, String label) {
private String[] getArgs(String application, String profile, String label) {
List<String> list = new ArrayList<String>();
String config = application;
if (!config.startsWith("application")) {
config = "application," + config;
}
@@ -204,7 +221,7 @@ public class NativeEnvironmentRepository
list.add("--spring.cloud.bootstrap.enabled=false");
list.add("--encrypt.failOnError=" + this.failOnError);
list.add("--spring.config.location=" + StringUtils.arrayToCommaDelimitedString(
getLocations(null, null, label).getLocations()));
getLocations(application, profile, label).getLocations()));
return list.toArray(new String[0]);
}
@@ -234,8 +251,8 @@ public class NativeEnvironmentRepository
}
private boolean isDirectory(String location) {
return !location.endsWith(".properties") && !location.endsWith(".yml")
&& !location.endsWith(".yaml");
return !location.contains("{") && !location.endsWith(".properties")
&& !location.endsWith(".yml") && !location.endsWith(".yaml");
}
}

View File

@@ -56,7 +56,7 @@ public class JGitEnvironmentRepositoryIntegrationTests {
if (this.basedir.exists()) {
FileUtils.delete(this.basedir, FileUtils.RECURSIVE);
}
ConfigServerTestUtils.deleteLocalRepo("config-copy");
ConfigServerTestUtils.deleteLocalRepo("");
}
@After

View File

@@ -0,0 +1,126 @@
/*
* 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.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
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.test.ConfigServerTestUtils;
import org.springframework.core.env.StandardEnvironment;
/**
* @author Dave Syer
*
*/
public class MultipleJGitEnvironmentUriTemplateRepositoryTests {
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<String, PatternMatchingJGitEnvironmentRepository> createRepositories()
throws Exception {
String test1Uri = ConfigServerTestUtils.prepareLocalRepo("test1-config-repo");
ConfigServerTestUtils.prepareLocalRepo("test2-config-repo");
Map<String, PatternMatchingJGitEnvironmentRepository> repos = new HashMap<>();
repos.put("templates", createRepository("test", "*-config-repo",
test1Uri.replace("test1-config-repo", "{application}")));
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("test1-config-repo", "staging",
"master");
assertEquals(1, environment.getPropertySources().size());
assertEquals(
getUri("*").replace("{application}", "test1-config-repo")
+ "/application.yml",
environment.getPropertySources().get(0).getName());
assertVersion(environment);
}
@Test
public void otherMappingRepo() {
Environment environment = this.repository.findOne("test2-config-repo", "staging",
"master");
assertEquals(1, environment.getPropertySources().size());
assertEquals(
getUri("*").replace("{application}", "test2-config-repo")
+ "/application.properties",
environment.getPropertySources().get(0).getName());
assertVersion(environment);
}
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<String, PatternMatchingJGitEnvironmentRepository> 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;
}
}

View File

@@ -21,6 +21,7 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.server.environment.SearchPathLocator.Locations;
import org.springframework.context.ConfigurableApplicationContext;
/**
@@ -43,7 +44,7 @@ public class NativeEnvironmentRepositoryTests {
@Test
public void emptySearchLocations() {
this.repository.setSearchLocations((String[])null);
this.repository.setSearchLocations((String[]) null);
Environment environment = this.repository.findOne("foo", "development", "master");
assertEquals(2, environment.getPropertySources().size());
}
@@ -91,4 +92,48 @@ public class NativeEnvironmentRepositoryTests {
assertEquals("version was wrong", "myversion", environment.getVersion());
}
@Test
public void placeholdersLabel() {
this.repository.setSearchLocations("classpath:/test/{label}/");
Environment environment = this.repository.findOne("foo", "development", "dev");
assertEquals(1, environment.getPropertySources().size());
assertEquals("dev_bar",
environment.getPropertySources().get(0).getSource().get("foo"));
}
@Test
public void placeholdersProfile() {
this.repository.setSearchLocations("classpath:/test/{profile}/");
Environment environment = this.repository.findOne("foo", "dev", "master");
assertEquals(1, environment.getPropertySources().size());
assertEquals("dev_bar",
environment.getPropertySources().get(0).getSource().get("foo"));
}
@Test
public void placeholdersApplicationAndProfile() {
this.repository.setSearchLocations("classpath:/test/{profile}/{application}/");
Environment environment = this.repository.findOne("app", "dev", "master");
assertEquals(1, environment.getPropertySources().size());
assertEquals("app",
environment.getPropertySources().get(0).getSource().get("foo"));
}
@Test
public void locationPlaceholdersApplication() {
this.repository.setSearchLocations("classpath:/test/{application}");
Locations locations = this.repository.getLocations("foo", "dev", "master");
assertEquals(1, locations.getLocations().length);
assertEquals("classpath:/test/foo/", locations.getLocations()[0]);
}
@Test
public void placeholdersNoTrailingSlash() {
this.repository.setSearchLocations("classpath:/test/{label}");
Environment environment = this.repository.findOne("foo", "development", "dev");
assertEquals(1, environment.getPropertySources().size());
assertEquals("dev_bar",
environment.getPropertySources().get(0).getSource().get("foo"));
}
}

View File

@@ -0,0 +1,8 @@
Add application.properties
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
# new file: application.properties
# deleted: application.yml
#

View File

@@ -0,0 +1,2 @@
7df4a26d5437d9d4090cd5809967f870444cde8f not-for-merge branch 'raw' of dsyer@localhost:/home/dsyer/dev/platform/config/spring-platform-config-server/target/test-classes/config-repo
7df4a26d5437d9d4090cd5809967f870444cde8f not-for-merge tag 'foo' of dsyer@localhost:/home/dsyer/dev/platform/config/spring-platform-config-server/target/test-classes/config-repo

View File

@@ -0,0 +1 @@
ref: refs/heads/master

View File

@@ -0,0 +1 @@
dfd1f2f006c9ea71be688e3eeaf383ff640fd21a

View File

@@ -0,0 +1,5 @@
[core]
repositoryformatversion = 0
filemode = true
logallrefupdates = true
[branch "raw"]

View File

@@ -0,0 +1,3 @@
1 1
7df4a26d5437d9d4090cd5809967f870444cde8f 9f01fb972bc9617e4ea59f5c8ee3ceb5ff515cd0 9f01fb972bc9617e4ea59f5c8ee3ceb5ff515cd0
1

View File

@@ -0,0 +1,8 @@
0000000000000000000000000000000000000000 7df4a26d5437d9d4090cd5809967f870444cde8f Dave Syer <dsyer@gopivotal.com> 1406860717 -0700
7df4a26d5437d9d4090cd5809967f870444cde8f 9f01fb972bc9617e4ea59f5c8ee3ceb5ff515cd0 Dave Syer <dsyer@pivotal.io> 1415902155 +0000 checkout: moving from raw to master
9f01fb972bc9617e4ea59f5c8ee3ceb5ff515cd0 c4bd92016dc14b9fe376d9c3f7af1c9d22d44ee4 Dave Syer <dsyer@pivotal.io> 1415902338 +0000 commit: Move application.yml to subdir
c4bd92016dc14b9fe376d9c3f7af1c9d22d44ee4 dfd1f2f006c9ea71be688e3eeaf383ff640fd21a Dave Syer <dsyer@pivotal.io> 1449047214 +0000 commit: Fix
dfd1f2f006c9ea71be688e3eeaf383ff640fd21a c4bd92016dc14b9fe376d9c3f7af1c9d22d44ee4 Dave Syer <dsyer@pivotal.io> 1449047236 +0000 rebase -i (start): checkout 9f01f
c4bd92016dc14b9fe376d9c3f7af1c9d22d44ee4 dfe21d586fc45ba72db173421c5139e26e50a7af Dave Syer <dsyer@pivotal.io> 1449047236 +0000 rebase -i (squash): Move application.yml to subdir
dfe21d586fc45ba72db173421c5139e26e50a7af dfe21d586fc45ba72db173421c5139e26e50a7af Dave Syer <dsyer@pivotal.io> 1449047243 +0000 rebase -i (finish): returning to refs/heads/master
dfe21d586fc45ba72db173421c5139e26e50a7af 29f2b1020f4b87e1de6aab368ff496cc4e99a959 Dave Syer <dsyer@pivotal.io> 1449047248 +0000 commit (amend): Add application.properties

View File

@@ -0,0 +1,5 @@
0000000000000000000000000000000000000000 9f01fb972bc9617e4ea59f5c8ee3ceb5ff515cd0 Dave Syer <dsyer@gopivotal.com> 1406860776 -0700 branch: Created from 9f01fb972bc9617e4ea59f5c8ee3ceb5ff515cd0
9f01fb972bc9617e4ea59f5c8ee3ceb5ff515cd0 c4bd92016dc14b9fe376d9c3f7af1c9d22d44ee4 Dave Syer <dsyer@pivotal.io> 1415902338 +0000 commit: Move application.yml to subdir
c4bd92016dc14b9fe376d9c3f7af1c9d22d44ee4 dfd1f2f006c9ea71be688e3eeaf383ff640fd21a Dave Syer <dsyer@pivotal.io> 1449047214 +0000 commit: Fix
dfd1f2f006c9ea71be688e3eeaf383ff640fd21a dfe21d586fc45ba72db173421c5139e26e50a7af Dave Syer <dsyer@pivotal.io> 1449047243 +0000 rebase -i (finish): refs/heads/master onto c4bd92016dc14b9fe376d9c3f7af1c9d22d44ee4
dfe21d586fc45ba72db173421c5139e26e50a7af 29f2b1020f4b87e1de6aab368ff496cc4e99a959 Dave Syer <dsyer@pivotal.io> 1449047248 +0000 commit (amend): Add application.properties

View File

@@ -0,0 +1 @@
0000000000000000000000000000000000000000 7df4a26d5437d9d4090cd5809967f870444cde8f Dave Syer <dsyer@gopivotal.com> 1406860717 -0700

View File

@@ -0,0 +1 @@
x+)JMU01f040031QH,(<28><>LN,<2C><><EFBFBD>ӫ<EFBFBD><D3AB>ah<61>xH<78>S<EFBFBD>O<EFBFBD>}jI<6A><49><EFBFBD><EFBFBD><EFBFBD><EFBFBD><07> <01>x<12>

View File

@@ -0,0 +1 @@
x<01><>Kj1D<><44>)z0<><30>g<1B>=<3D>n,<2C>-!ˆ<>>"<22><>kSP<53><07><>z-<03><><EFBFBD><EFBFBD>E@jR<6A><52>#<23><>d<EFBFBD><64><13>I<EFBFBD><49><EFBFBD><EFBFBD>U<EFBFBD><55><EFBFBD>`u<>

View File

@@ -0,0 +1,2 @@
x<01><>1
1E<>s<EFBFBD><73><05>$<24><>

View File

@@ -0,0 +1,2 @@
x<01><>K
1D]<5D><14> <0B>O;<3B><17><A<>Ӎ<01> 1z{<7B>7<EFBFBD>6<0F>WPT<50>97<39≯i<CCB8>Y<EFBFBD>3<EFBFBD>bM<62><4D>"<22><><EFBFBD>{%<25>1<EFBFBD>@"&<26>cPK<50><4B>h<EFBFBD>`J<><4A>Y<>P؇!!y Q,ar.p<><70><EFBFBD><EFBFBD>J<EFBFBD><EFBFBD>~<7E><>Czv<7A><76><EFBFBD><EFBFBD><16><>\<5C><><02><><EFBFBD>,<2C><><EFBFBD>Q<EFBFBD><><D7BA>N]<5D>[}<01><>A<EFBFBD>

View File

@@ -0,0 +1,2 @@
x<01><><EFBFBD>j1<10>S<EFBFBD>)<29>7<EFBFBD><37><EFBFBD><EFBFBD> LH<4C>6U<36>`o<>Kw<>!<21><06>}DH<44>i<06><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFA583><EFBFBD>7P<><50><EFBFBD>b<EFBFBD><62>9%Y<><59>D#Ҹ<><<3C>*Rp<12>AMn<1D><><EFBFBD>5<EFBFBD>n<EFBFBD>4<EFBFBD>Y<EFBFBD>PLy<11>,k<1C><><EFBFBD><EFBFBD>У<D0A3>Wz
|<7C><><EFBFBD>%߇}<1C>Y;m<>R<EFBFBD><52>:<3A>8<><38><EFBFBD><EFBFBD>w}<7D><><EFBFBD>B<EFBFBD>0;?<3F>q泎:<3A><>0<EFBFBD>Ro<52>׾A<D7BE>p<70><7F>4<EFBFBD>V LZ

View File

@@ -0,0 +1 @@
29f2b1020f4b87e1de6aab368ff496cc4e99a959

View File

@@ -0,0 +1 @@
7df4a26d5437d9d4090cd5809967f870444cde8f

View File

@@ -0,0 +1 @@
7df4a26d5437d9d4090cd5809967f870444cde8f