Add GIT repository support

Default to a scratch repository on github:

$ curl localhost:8888/foo/development
{"name":"development","label":"master","propertySources":[
  {"name":"https://github.com/scratches/config-repo/foo-development.properties","source":{"bar":"spam"}},
  {"name":"https://github.com/scratches/config-repo/foo.properties","source":{"foo":"bar"}}
]}
This commit is contained in:
Dave Syer
2014-06-14 14:19:35 +01:00
parent 06bee89703
commit 5dbf017d99
11 changed files with 298 additions and 5 deletions

View File

@@ -59,6 +59,9 @@ public class BootstrapApplicationListener implements
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
Environment environment = event.getEnvironment();
if (!environment.getProperty("spring.platform.bootstrap.enabled", Boolean.class, true)) {
return;
}
if (environment instanceof ConfigurableEnvironment) {
ConfigurableEnvironment configurable = (ConfigurableEnvironment) environment;
// don't listen to events in a bootstrap context

View File

@@ -7,6 +7,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.platform.bootstrap.config.Environment;
import org.springframework.web.bind.annotation.PathVariable;
@@ -37,6 +38,7 @@ public class Application {
}
@Configuration
@Profile("native")
protected static class NativeRepositoryConfiguration {
@Autowired
private ConfigurableEnvironment environment;
@@ -46,4 +48,13 @@ public class Application {
return new NativeEnvironmentRepository(environment);
}
}
@Configuration
@Profile("!native")
protected static class GitRepositoryConfiguration {
@Bean
public JGitEnvironmentRepository repository() {
return new JGitEnvironmentRepository();
}
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2013-2014 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.platform.config.server;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import org.eclipse.jgit.api.Git;
import org.springframework.platform.bootstrap.config.Environment;
import org.springframework.platform.bootstrap.config.PropertySource;
/**
* @author Dave Syer
*
*/
public class JGitEnvironmentRepository implements EnvironmentRepository {
public static final String DEFAULT_URI = "https://github.com/scratches/config-repo";
private File basedir;
private String uri = DEFAULT_URI;
public JGitEnvironmentRepository() {
try {
basedir = Files.createTempDirectory("config-repo-").toFile();
basedir.deleteOnExit();
} catch (IOException e) {
throw new IllegalStateException("Cannot create temp dir", e);
}
}
public void setUri(String uri) {
while (uri.endsWith("/")) {
uri = uri.substring(0, uri.length() - 1);
}
this.uri = uri;
}
@Override
public Environment findOne(String application, String name, String label) {
try {
Git git;
if (new File(basedir, ".git").exists()) {
git = Git.open(basedir);
} else {
git = Git.cloneRepository().setURI(uri).setDirectory(basedir).call();
}
Environment result;
synchronized (this) {
SpringApplicationEnvironmentRepository environment = new SpringApplicationEnvironmentRepository();
git.checkout().setName(label).call();
String search = git.getRepository().getDirectory().getParent();
environment.setSearchLocations(search);
result = clean(environment.findOne(application, name, label));
}
return result;
} catch (Exception e) {
throw new IllegalStateException("Cannot clone repository", e);
}
}
private Environment clean(Environment value) {
Environment result = new Environment(value.getName(), value.getLabel());
for (PropertySource source : value.getPropertySources()) {
String name = source.getName().replace(basedir.toURI().toString(), "");
name = name.replace("applicationConfig: [", "");
name = uri + "/"
+ name.substring(0, name.contains("]") ? name.lastIndexOf("]") : name.length());
result.add(new PropertySource(name, source.getSource()));
}
return result;
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.platform.config.server;
import java.util.Arrays;
@@ -27,8 +28,10 @@ import org.springframework.platform.bootstrap.config.Environment;
import org.springframework.platform.bootstrap.config.PropertySource;
import org.springframework.web.context.support.StandardServletEnvironment;
/**
* Simple implementation of {@link EnvironmentRepository} that just reflects an existing
* Spring Environment.
*
* @author Dave Syer
*
*/
@@ -46,14 +49,14 @@ public class NativeEnvironmentRepository implements EnvironmentRepository {
public NativeEnvironmentRepository(ConfigurableEnvironment environment) {
this.environment = environment;
}
@Override
public Environment findOne(String application, String env, String label) {
Environment result = new Environment(env, label);
for (org.springframework.core.env.PropertySource<?> source : environment.getPropertySources()) {
String name = source.getName();
if (!standardSources .contains(name) && source instanceof MapPropertySource) {
result.add(new PropertySource(name, (Map<?,?>)source.getSource()));
if (!standardSources.contains(name) && source instanceof MapPropertySource) {
result.add(new PropertySource(name, (Map<?, ?>) source.getSource()));
}
}
return result;

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2013-2014 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.platform.config.server;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.platform.bootstrap.config.Environment;
import org.springframework.util.StringUtils;
/**
* Simple implementation of {@link EnvironmentRepository} that uses a SpringApplication
* and configuration files located through the normal protocols. The resulting Environment
* is composed of property sources located using the application name as the config file
* stem (spring.config.name) and the environment name as a Spring profile.
*
* @author Dave Syer
*
*/
public class SpringApplicationEnvironmentRepository implements EnvironmentRepository {
private String[] locations;
@Override
public Environment findOne(String config, String profile, String label) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(
PropertyPlaceholderAutoConfiguration.class);
builder.profiles(profile.split(",")).web(false).showBanner(false);
String[] args = getArgs(config);
ConfigurableApplicationContext context = builder.run(args);
try {
return new NativeEnvironmentRepository(context.getEnvironment()).findOne(
config, profile, label);
} finally {
context.close();
}
}
private String[] getArgs(String config) {
List<String> list = new ArrayList<String>();
list.add("--spring.config.name=" + config);
list.add("--spring.platform.bootstrap.enabled=false");
if (locations != null) {
list.add("--spring.config.location="
+ StringUtils.arrayToCommaDelimitedString(locations));
}
return list.toArray(new String[0]);
}
public void setSearchLocations(String... locations) {
this.locations = locations;
for (int i = 0; i < locations.length; i++) {
String location = locations[i];
if (!location.endsWith(".properties") && !location.endsWith(".yml")
&& !location.endsWith(".yaml") && !location.endsWith("/")) {
location = location + "/";
}
locations[i] = location;
}
}
}

View File

@@ -1,18 +1,30 @@
package org.springframework.platform.config.server;
import static org.junit.Assert.assertFalse;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate;
import org.springframework.platform.bootstrap.config.Environment;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@IntegrationTest("server.port:0")
@WebAppConfiguration
public class ApplicationTests {
@Value("${local.server.port}")
private int port;
@Test
public void contextLoads() {
Environment environment = new TestRestTemplate().getForObject("http://localhost:" + port + "/foo/development/", Environment.class);
assertFalse(environment.getPropertySources().isEmpty());
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2013-2014 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.platform.config.server;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.platform.bootstrap.config.Environment;
/**
* @author Dave Syer
*
*/
public class JGitEnvironmentRepositoryTests {
private JGitEnvironmentRepository repository = new JGitEnvironmentRepository();
@Test
public void vanilla() {
repository.findOne("bar", "staging", "master");
Environment environment = repository.findOne("bar", "staging", "master");
assertEquals(1, environment.getPropertySources().size());
assertEquals(JGitEnvironmentRepository.DEFAULT_URI + "/bar.properties",
environment.getPropertySources().get(0).getName());
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2013-2014 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.platform.config.server;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.platform.bootstrap.config.Environment;
/**
* @author Dave Syer
*
*/
public class SpringApplicationEnvironmentRepositoryTests {
private SpringApplicationEnvironmentRepository repository = new SpringApplicationEnvironmentRepository();
@Test
public void vanilla() {
Environment environment = repository.findOne("foo", "development", "master");
assertEquals(2, environment.getPropertySources().size());
}
@Test
public void prefixed() {
repository.setSearchLocations("classpath:/test");
Environment environment = repository.findOne("foo", "development", "master");
assertEquals(3, environment.getPropertySources().size());
}
@Test
public void prefixedWithFile() {
repository.setSearchLocations("file:./src/test/resources/test");
Environment environment = repository.findOne("foo", "development", "master");
assertEquals(3, environment.getPropertySources().size());
}
}

View File

@@ -0,0 +1 @@
bar: spam

View File

@@ -0,0 +1 @@
foo: bar

View File

@@ -0,0 +1 @@
foo: test_bar