Add sample app
This commit is contained in:
46
README.md
46
README.md
@@ -1,13 +1,15 @@
|
||||
Spring Platform Config provides server and client-side support for
|
||||
externalized configuration in a distributed system. With the Config
|
||||
Server you have a central place to manage external properties for the
|
||||
applications in your origanization, across all environments. The
|
||||
concepts on both sides map identically to the Spring `Environment` and
|
||||
`PropertySource` abstractions, so they fit very well with Spring
|
||||
applications. As an application moves through the deployment pipeline
|
||||
from dev to test and into production you can manage the configuration
|
||||
that needs to change between those environments and be certain that
|
||||
applications have everything they need to run when they migrate.
|
||||
applications across all environments. The concepts on both sides map
|
||||
identically to the Spring `Environment` and `PropertySource`
|
||||
abstractions, so they fit very well with Spring applications. As an
|
||||
application moves through the deployment pipeline from dev to test and
|
||||
into production you can manage the configuration that needs to change
|
||||
between those environments and be certain that applications have
|
||||
everything they need to run when they migrate. The default
|
||||
implementation of the server repository strategy uses git as a storage
|
||||
backend so it easily supports labelled versions of configurations.
|
||||
|
||||
## Quick Start
|
||||
|
||||
@@ -18,7 +20,9 @@ $ cd spring-platform-config-server
|
||||
$ mvn spring-boot:run
|
||||
```
|
||||
|
||||
Then try it:
|
||||
The server is a Spring Boot application so you can build the jar file
|
||||
and run that (`java -jar ...`) or pull it down from a Maven
|
||||
repository. Then try it out as a client:
|
||||
|
||||
```
|
||||
$ curl localhost:8888/foo/development
|
||||
@@ -28,7 +32,7 @@ $ curl localhost:8888/foo/development
|
||||
]}
|
||||
```
|
||||
|
||||
The default strategy for locating property sources is to clone a GIT
|
||||
The default strategy for locating property sources is to clone a git
|
||||
repository (at "spring.platform.config.uri") and use it to initialize
|
||||
a `SpringApplication`. The application's `Environment` is used to
|
||||
enumerate property sources. The service has resources in the form:
|
||||
@@ -38,17 +42,18 @@ enumerate property sources. The service has resources in the form:
|
||||
```
|
||||
|
||||
where the "name" is used as the config name in the `SpringApplication`
|
||||
(i.e. what is normally "application"), "profile" is an active profile
|
||||
(or comma-separated list of properties), and "label" is an optional
|
||||
git label (defaults to "master").
|
||||
(i.e. what is normally "application" in a regular Spring Boot app),
|
||||
"profile" is an active profile (or comma-separated list of
|
||||
properties), and "label" is an optional git label (defaults to
|
||||
"master").
|
||||
|
||||
### Client Side Usage
|
||||
|
||||
Build a client (e.g. see the test cases for the config-client) as a
|
||||
Spring Boot application that depends on spring-platform-config-client.
|
||||
When it runs it will pick up the external configuration from the
|
||||
default local config server on port 8888. To modify the startup
|
||||
behaviour change the location of the server using
|
||||
default local config server on port 8888 if it is running. To modify
|
||||
the startup behaviour you can change the location of the server using
|
||||
`bootstrap.properties` (like `application.properties` but for the
|
||||
bootstrap phase of an application context), e.g.
|
||||
|
||||
@@ -56,4 +61,15 @@ bootstrap phase of an application context), e.g.
|
||||
spring.platform.config.url: http://myconfigserver.com
|
||||
```
|
||||
|
||||
TODO: add a sample app.
|
||||
## Sample Application
|
||||
|
||||
There is a sample application
|
||||
[here](https://github.com/spring-platform/spring-platform-config/spring-platform-config-sample). It
|
||||
is a Spring Boot application so you can run it using the usual
|
||||
mechanisms (for instance "mvn spring-boot:run"). When it runs it will
|
||||
look for the config server on "http://localhost:8888" by default, so
|
||||
you could run the server as well to see it all working together.
|
||||
|
||||
The sample has a tets case where the config server is also started in
|
||||
the same JVM (with a different port), and the test asserts that an
|
||||
environment property from the git configuration repo is present.
|
||||
|
||||
22
pom.xml
22
pom.xml
@@ -14,6 +14,11 @@
|
||||
<version>1.1.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<modules>
|
||||
<module>spring-platform-config-client</module>
|
||||
<module>spring-platform-config-server</module>
|
||||
<module>spring-platform-config-sample</module>
|
||||
</modules>
|
||||
<organization>
|
||||
<name>Pivotal Software, Inc.</name>
|
||||
<url>http://www.spring.io</url>
|
||||
@@ -67,21 +72,6 @@
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>default</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>spring-platform-config-client</module>
|
||||
<module>spring-platform-config-server</module>
|
||||
</modules>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>full</id>
|
||||
</profile>
|
||||
</profiles>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@@ -91,7 +81,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.platform</groupId>
|
||||
<artifactId>spring-platform-config-client</artifactId>
|
||||
<artifactId>spring-platform-config-server</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -38,14 +38,14 @@ public class ConfigServicePropertySourceLocator implements PropertySourceLocator
|
||||
|
||||
private String label = "master";
|
||||
|
||||
private String url = "http://localhost:8888";
|
||||
private String uri = "http://localhost:8888";
|
||||
|
||||
private RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
@Override
|
||||
public org.springframework.core.env.PropertySource<?> locate() {
|
||||
CompositePropertySource composite = new CompositePropertySource("configService");
|
||||
Environment result = restTemplate.exchange(url + "/{name}/{env}/{label}",
|
||||
Environment result = restTemplate.exchange(uri + "/{name}/{env}/{label}",
|
||||
HttpMethod.GET, new HttpEntity<Void>((Void) null), Environment.class,
|
||||
name, env, label).getBody();
|
||||
for (PropertySource source : result.getPropertySources()) {
|
||||
@@ -56,12 +56,12 @@ public class ConfigServicePropertySourceLocator implements PropertySourceLocator
|
||||
return composite;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
public void setUri(String url) {
|
||||
this.uri = url;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
||||
66
spring-platform-config-sample/pom.xml
Normal file
66
spring-platform-config-sample/pom.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-platform-config-sample</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>spring-platform-config-sample</name>
|
||||
<description>spring-platform-config-server</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.platform</groupId>
|
||||
<artifactId>spring-platform-config</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.platform</groupId>
|
||||
<artifactId>spring-platform-config-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.platform</groupId>
|
||||
<artifactId>spring-platform-config-server</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<start-class>org.springframework.platform.config.server.Application</start-class>
|
||||
<java.version>1.7</java.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<!--skip deploy (this is just a test module) -->
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
package sample;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@EnableAutoConfiguration
|
||||
public class Application {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
info:
|
||||
component: Config Sample
|
||||
@@ -0,0 +1,6 @@
|
||||
spring:
|
||||
application:
|
||||
name: bar
|
||||
platform:
|
||||
config:
|
||||
uri: http://localhost:${config.port:8888}
|
||||
@@ -0,0 +1,42 @@
|
||||
package sample;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.embedded.EmbeddedWebApplicationContext;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.boot.test.TestRestTemplate;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
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 {
|
||||
|
||||
private static int configPort;
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port;
|
||||
|
||||
@BeforeClass
|
||||
public static void startConfigServer() {
|
||||
ConfigurableApplicationContext context = SpringApplication.run(org.springframework.platform.config.server.Application.class, "--server.port=0");
|
||||
configPort = ((EmbeddedWebApplicationContext)context).getEmbeddedServletContainer().getPort();
|
||||
System.setProperty("config.port", "" + configPort);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
String foo = new TestRestTemplate().getForObject("http://localhost:" + port + "/env/foo", String.class);
|
||||
assertEquals("bar", foo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,9 +20,13 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.util.FileUtils;
|
||||
import org.springframework.platform.bootstrap.config.Environment;
|
||||
import org.springframework.platform.bootstrap.config.PropertySource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
@@ -31,6 +35,8 @@ import org.springframework.platform.bootstrap.config.PropertySource;
|
||||
public class JGitEnvironmentRepository implements EnvironmentRepository {
|
||||
|
||||
public static final String DEFAULT_URI = "https://github.com/scratches/config-repo";
|
||||
|
||||
private static Log logger = LogFactory.getLog(JGitEnvironmentRepository.class);
|
||||
|
||||
private File basedir;
|
||||
|
||||
@@ -38,8 +44,18 @@ public class JGitEnvironmentRepository implements EnvironmentRepository {
|
||||
|
||||
public JGitEnvironmentRepository() {
|
||||
try {
|
||||
basedir = Files.createTempDirectory("config-repo-").toFile();
|
||||
basedir.deleteOnExit();
|
||||
final File basedir = Files.createTempDirectory("config-repo-").toFile();
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
FileUtils.delete(basedir, FileUtils.RECURSIVE);
|
||||
} catch (IOException e) {
|
||||
logger.warn("Failed to delete temporary directory on exit: " + e);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.basedir = basedir;
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Cannot create temp dir", e);
|
||||
}
|
||||
@@ -51,6 +67,11 @@ public class JGitEnvironmentRepository implements EnvironmentRepository {
|
||||
}
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
|
||||
public void setBasedir(File basedir) {
|
||||
this.basedir = basedir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String name, String label) {
|
||||
@@ -59,6 +80,14 @@ public class JGitEnvironmentRepository implements EnvironmentRepository {
|
||||
if (new File(basedir, ".git").exists()) {
|
||||
git = Git.open(basedir);
|
||||
} else {
|
||||
if (basedir.exists()) {
|
||||
try {
|
||||
FileUtils.delete(basedir, FileUtils.RECURSIVE);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to initialize base directory", e);
|
||||
}
|
||||
}
|
||||
Assert.state(basedir.mkdirs(), "Could not create basedir: " + basedir);
|
||||
git = Git.cloneRepository().setURI(uri).setDirectory(basedir).call();
|
||||
}
|
||||
Environment result;
|
||||
|
||||
@@ -16,8 +16,13 @@
|
||||
|
||||
package org.springframework.platform.config.server;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.eclipse.jgit.util.FileUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.platform.bootstrap.config.Environment;
|
||||
|
||||
@@ -29,6 +34,15 @@ public class JGitEnvironmentRepositoryTests {
|
||||
|
||||
private JGitEnvironmentRepository repository = new JGitEnvironmentRepository();
|
||||
|
||||
private File basedir = new File("target/config-repo");
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
if (basedir.exists()) {
|
||||
FileUtils.delete(basedir, FileUtils.RECURSIVE);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vanilla() {
|
||||
repository.findOne("bar", "staging", "master");
|
||||
@@ -38,4 +52,26 @@ public class JGitEnvironmentRepositoryTests {
|
||||
environment.getPropertySources().get(0).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basedir() {
|
||||
repository.setBasedir(basedir);
|
||||
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());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basedirExists() throws Exception {
|
||||
assertTrue(basedir.mkdirs());
|
||||
assertTrue(new File(basedir, ".nothing").createNewFile());
|
||||
repository.setBasedir(basedir);
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user