Add ResourceController for serving plain text config files

Can be adapted to serve content in any format (e.g. nginx config file,
XML configuration for logger, etc.) - basically anything that can be
stored in plain text and doesn't require streaming.

Fixes gh-147, see also gh-198
This commit is contained in:
Dave Syer
2015-09-30 13:50:37 +01:00
parent 02a9eb3cd7
commit 62846bda84
27 changed files with 738 additions and 124 deletions

View File

@@ -0,0 +1,46 @@
/*
* Copyright 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;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* @author Dave Syer
*
*/
public abstract class AbstractScmEnvironmentRepository extends AbstractScmAccessor
implements EnvironmentRepository, ResourceLocationService {
private EnvironmentCleaner cleaner = new EnvironmentCleaner();
public AbstractScmEnvironmentRepository(ConfigurableEnvironment environment) {
super(environment);
}
@Override
public synchronized Environment findOne(String application, String profile, String label) {
NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(
getEnvironment());
delegate.setSearchLocations(getLocations(application, profile, label));
Environment result = delegate.findOne(application, profile, "");
result.setLabel(label);
return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(),
getUri());
}
}

View File

@@ -24,6 +24,7 @@ import java.lang.annotation.Target;
import org.springframework.cloud.config.server.config.ConfigServerEncryptionConfiguration;
import org.springframework.cloud.config.server.config.ConfigServerMvcConfiguration;
import org.springframework.cloud.config.server.config.EnvironmentRepositoryConfiguration;
import org.springframework.cloud.config.server.config.ResourceRepositoryConfiguration;
import org.springframework.context.annotation.Import;
/**
@@ -33,7 +34,7 @@ import org.springframework.context.annotation.Import;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({ EnvironmentRepositoryConfiguration.class,
@Import({ EnvironmentRepositoryConfiguration.class, ResourceRepositoryConfiguration.class,
ConfigServerEncryptionConfiguration.class, ConfigServerMvcConfiguration.class })
public @interface EnableConfigServer {

View File

@@ -74,6 +74,10 @@ public class EnvironmentController {
private boolean stripDocument = true;
public EnvironmentController(EnvironmentRepository repository) {
this(repository, null);
}
public EnvironmentController(EnvironmentRepository repository,
EnvironmentEncryptor environmentEncryptor) {
this.repository = repository;

View File

@@ -17,7 +17,6 @@ package org.springframework.cloud.config.server;
import org.springframework.cloud.config.environment.Environment;
/**
* @author Dave Syer
* @author Roy Clarkson

View File

@@ -0,0 +1,94 @@
/*
* 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;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.StringUtils;
/**
* An {@link ResourceRepository} backed by a {@link ResourceLocationService}.
*
* @author Dave Syer
*/
public class GenericResourceRepository
implements ResourceRepository, ResourceLoaderAware {
private ResourceLoader resourceLoader;
private ResourceLocationService service;
public GenericResourceRepository(ResourceLocationService service) {
this.service = service;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public synchronized Resource findOne(String application, String profile, String label,
String path) {
String[] locations = this.service.getLocations(application, "default", label);
try {
for (int i = locations.length; i-- > 0;) {
String location = locations[i];
for (String local : getProfilePaths(profile, path)) {
Resource file = this.resourceLoader.getResource(location)
.createRelative(local);
if (file.exists() && file.isReadable()) {
return file;
}
}
}
}
catch (IOException e) {
throw new NoSuchResourceException(
"Error : " + path + ". (" + e.getMessage() + ")");
}
throw new NoSuchResourceException("Not found: " + path);
}
private Collection<String> getProfilePaths(String profiles, String path) {
Set<String> paths = new LinkedHashSet<>();
for (String profile : StringUtils.commaDelimitedListToSet(profiles)) {
if (!StringUtils.hasText(profile) || "default".equals(profile)) {
paths.add(path);
}
else {
String ext = StringUtils.getFilenameExtension(path);
if (ext != null) {
ext = "." + ext;
path = StringUtils.stripFilenameExtension(path);
}
else {
ext = "";
}
paths.add(path + "-" + profile + ext);
}
}
return paths;
}
}

View File

@@ -39,7 +39,6 @@ import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.eclipse.jgit.util.FileUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.UrlResource;
import org.springframework.util.Assert;
@@ -53,13 +52,15 @@ import com.jcraft.jsch.Session;
* @author Dave Syer
* @author Roy Clarkson
*/
public class JGitEnvironmentRepository extends AbstractScmAccessor implements EnvironmentRepository, InitializingBean {
public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
implements EnvironmentRepository, ResourceLocationService, InitializingBean {
private static final String DEFAULT_LABEL = "master";
private static final String FILE_URI_PREFIX = "file:";
/**
* Timeout (in seconds) for obtaining HTTP or SSH connection (if applicable). Default 5 seconds.
* Timeout (in seconds) for obtaining HTTP or SSH connection (if applicable). Default
* 5 seconds.
*/
private int timeout = 5;
@@ -71,8 +72,6 @@ public class JGitEnvironmentRepository extends AbstractScmAccessor implements En
*/
private boolean cloneOnStart = false;
private EnvironmentCleaner cleaner = new EnvironmentCleaner();
private JGitEnvironmentRepository.JGitFactory gitFactory = new JGitEnvironmentRepository.JGitFactory();
public JGitEnvironmentRepository(ConfigurableEnvironment environment) {
@@ -109,12 +108,33 @@ public class JGitEnvironmentRepository extends AbstractScmAccessor implements En
}
@Override
public Environment findOne(String application, String profile, String label) {
public String[] getLocations(String application, String profile, String label) {
refresh(application, label);
return getSearchLocations(getWorkingDirectory());
}
@Override
public void afterPropertiesSet() throws Exception {
Assert.state(getUri() != null,
"You need to configure a uri for the git repository");
if (this.cloneOnStart) {
initClonedRepository();
}
}
/**
* Get the working directory ready.
*/
private void refresh(String application, String label) {
initialize();
Git git = null;
try {
git = createGitClient();
return loadEnvironment(git, application, profile, label);
git.getRepository().getConfig().setString("branch", label, "merge", label);
Ref ref = checkout(git, label);
if (shouldPull(git, ref)) {
pull(git, label, ref);
}
}
catch (RefNotFoundException e) {
throw new NoSuchLabelException("No such label: " + label);
@@ -137,15 +157,6 @@ public class JGitEnvironmentRepository extends AbstractScmAccessor implements En
}
}
@Override
public void afterPropertiesSet() throws Exception {
Assert.state(getUri() != null,
"You need to configure a uri for the git repository");
if (this.cloneOnStart) {
initClonedRepository();
}
}
/**
* Clones the remote repository and then opens a connection to it.
* @throws GitAPIException
@@ -160,21 +171,6 @@ public class JGitEnvironmentRepository extends AbstractScmAccessor implements En
}
private synchronized Environment loadEnvironment(Git git, String application,
String profile, String label) throws GitAPIException {
NativeEnvironmentRepository environment = new NativeEnvironmentRepository(
getEnvironment());
git.getRepository().getConfig().setString("branch", label, "merge", label);
Ref ref = checkout(git, label);
if (shouldPull(git, ref)) {
pull(git, label, ref);
}
environment.setSearchLocations(getSearchLocations(getWorkingDirectory()));
Environment result = environment.findOne(application, profile, "");
result.setLabel(label);
return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(), getUri());
}
private Ref checkout(Git git, String label) throws GitAPIException {
CheckoutCommand checkout = git.checkout();
if (shouldTrack(git, label)) {
@@ -299,8 +295,8 @@ public class JGitEnvironmentRepository extends AbstractScmAccessor implements En
private void trackBranch(Git git, CheckoutCommand checkout, String label) {
checkout.setCreateBranch(true).setName(label)
.setUpstreamMode(SetupUpstreamMode.TRACK)
.setStartPoint("origin/" + label);
.setUpstreamMode(SetupUpstreamMode.TRACK)
.setStartPoint("origin/" + label);
}
private boolean isBranch(Git git, String label) throws GitAPIException {

View File

@@ -79,6 +79,17 @@ public class MultipleJGitEnvironmentRepository extends JGitEnvironmentRepository
return this.repos;
}
@Override
public String[] 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);
}
}
return super.getLocations(application, profile, label);
}
@Override
public Environment findOne(String application, String profile, String label) {
for (PatternMatchingJGitEnvironmentRepository repository : this.repos.values()) {

View File

@@ -47,7 +47,8 @@ import org.springframework.util.StringUtils;
* @author Roy Clarkson
*/
@ConfigurationProperties("spring.cloud.config.server.native")
public class NativeEnvironmentRepository implements EnvironmentRepository {
public class NativeEnvironmentRepository
implements EnvironmentRepository, ResourceLocationService {
private static Log logger = LogFactory.getLog(NativeEnvironmentRepository.class);
@@ -96,8 +97,8 @@ public class NativeEnvironmentRepository implements EnvironmentRepository {
String[] args = getArgs(config, label);
// Explicitly set the listeners (to exclude logging listener which would change
// log levels in the caller)
builder.application().setListeners(
Arrays.asList(new ConfigFileEnvironmentPostProcessor(),
builder.application()
.setListeners(Arrays.asList(new ConfigFileEnvironmentPostProcessor(),
new EnvironmentPostProcessingApplicationListener()));
ConfigurableApplicationContext context = builder.run(args);
environment.getPropertySources().remove("profiles");
@@ -110,13 +111,30 @@ public class NativeEnvironmentRepository implements EnvironmentRepository {
}
}
@Override
public String[] getLocations(String application, String profile, String label) {
String[] locations = this.searchLocations;
if (this.searchLocations == null) {
locations = DEFAULT_LOCATIONS;
}
List<String> output = new ArrayList<String>();
for (String location : locations) {
output.add(location);
}
for (String location : locations) {
if (isDirectory(location) && StringUtils.hasText(label)) {
output.add(location + label.trim() + "/");
}
}
return output.toArray(new String[0]);
}
private ConfigurableEnvironment getEnvironment(String profile) {
ConfigurableEnvironment environment = new StandardEnvironment();
environment.getPropertySources()
.addFirst(
new MapPropertySource("profiles", Collections
.<String, Object> singletonMap("spring.profiles.active",
profile)));
.addFirst(new MapPropertySource("profiles",
Collections.<String, Object> singletonMap(
"spring.profiles.active", profile)));
return environment;
}
@@ -134,18 +152,19 @@ public class NativeEnvironmentRepository implements EnvironmentRepository {
boolean matches = false;
String normal = name;
if (normal.startsWith("file:")) {
normal = StringUtils.cleanPath(new File(normal.substring("file:"
.length())).getAbsolutePath());
normal = StringUtils
.cleanPath(new File(normal.substring("file:".length()))
.getAbsolutePath());
}
for (String pattern : StringUtils
.commaDelimitedListToStringArray(getLocations(
this.searchLocations, result.getLabel()))) {
for (String pattern : getLocations(null, null, result.getLabel())) {
if (!pattern.contains(":")) {
pattern = "file:" + pattern;
}
if (pattern.startsWith("file:")) {
pattern = StringUtils.cleanPath(new File(pattern
.substring("file:".length())).getAbsolutePath()) + "/";
pattern = StringUtils
.cleanPath(new File(pattern.substring("file:".length()))
.getAbsolutePath())
+ "/";
}
if (logger.isTraceEnabled()) {
logger.trace("Testing pattern: " + pattern
@@ -179,27 +198,10 @@ public class NativeEnvironmentRepository implements EnvironmentRepository {
list.add("--spring.config.name=" + config);
list.add("--spring.cloud.bootstrap.enabled=false");
list.add("--encrypt.failOnError=" + this.failOnError);
String[] locations = this.searchLocations;
if (this.searchLocations == null) {
locations = DEFAULT_LOCATIONS;
}
list.add("--spring.config.location=" + getLocations(locations, label));
list.add("--spring.config.location=" + StringUtils.arrayToCommaDelimitedString(getLocations(null, null, label)));
return list.toArray(new String[0]);
}
private String getLocations(String[] locations, String label) {
List<String> output = new ArrayList<String>();
for (String location : locations) {
output.add(location);
}
for (String location : locations) {
if (isDirectory(location) && StringUtils.hasText(label)) {
output.add(location + label.trim() + "/");
}
}
return StringUtils.collectionToCommaDelimitedString(output);
}
public String[] getSearchLocations() {
return this.searchLocations;
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 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;
/**
* @author Dave Syer
*
*/
public class NoSuchResourceException extends RuntimeException {
public NoSuchResourceException(String string) {
super(string);
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright 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;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Map;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
/**
* An HTTP endpoint for serving up templated plain text resources from an underlying
* repository. Can be used to supply config files for consumption by a wide variety of
* applications and services. A {@link ResourceRepository} is used to locate a
* {@link Resource}, specific to an application, and the contents are transformed to text.
* Then an {@link EnvironmentController} is used to supply key-value pairs which are used
* to replace placeholders in the resource text.
*
* @author Dave Syer
*
*/
@RestController
@RequestMapping(method = RequestMethod.GET, value = "${spring.cloud.config.server.prefix:}")
public class ResourceController {
private ResourceRepository resourceRepository;
private EnvironmentController environmentController;
public ResourceController(ResourceRepository resourceRepository,
EnvironmentController environmentController) {
this.resourceRepository = resourceRepository;
this.environmentController = environmentController;
}
@RequestMapping("/{name}/{profile}/{label}/{path:.*}")
public synchronized String resolve(@PathVariable String name,
@PathVariable String profile, @PathVariable String label,
@PathVariable String path) throws IOException {
StandardEnvironment environment = new StandardEnvironment();
environment.getPropertySources().addAfter(
StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME,
new EnvironmentPropertySource(
this.environmentController.labelled(name, profile, label)));
String text = StreamUtils.copyToString(
this.resourceRepository.findOne(name, profile, label, path).getInputStream(),
Charset.forName("UTF-8"));
// Mask out escaped placeholders
text = text.replace("\\${", "$_{");
return environment.resolvePlaceholders(text).replace("$_{", "${");
}
@ExceptionHandler(NoSuchResourceException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public void notFound(NoSuchResourceException e) {
}
private static class EnvironmentPropertySource extends PropertySource<Environment> {
public EnvironmentPropertySource(Environment sources) {
super("cloudEnvironment", sources);
}
@Override
public Object getProperty(String name) {
for (org.springframework.cloud.config.environment.PropertySource source : getSource()
.getPropertySources()) {
Map<?, ?> map = source.getSource();
if (map.containsKey(name)) {
return map.get(name);
}
}
return null;
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 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;
/**
* @author Dave Syer
*
*/
public interface ResourceLocationService {
String[] getLocations(String application, String profile, String label);
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 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;
import org.springframework.core.io.Resource;
/**
* @author Dave Syer
*
*/
public interface ResourceRepository {
Resource findOne(String name, String profile, String label, String path);
}

View File

@@ -23,7 +23,6 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -42,22 +41,20 @@ import org.tmatesoft.svn.core.wc2.SvnUpdate;
* @author Roy Clarkson
*/
@ConfigurationProperties("spring.cloud.config.server.svn")
public class SvnKitEnvironmentRepository extends AbstractScmAccessor
public class SvnKitEnvironmentRepository extends AbstractScmEnvironmentRepository
implements EnvironmentRepository, InitializingBean {
private static Log logger = LogFactory.getLog(SvnKitEnvironmentRepository.class);
private static final String DEFAULT_LABEL = "trunk";
private EnvironmentCleaner cleaner = new EnvironmentCleaner();
@Override
public String getDefaultLabel() {
return DEFAULT_LABEL;
}
@Override
public Environment findOne(String application, String profile, String label) {
public String[] getLocations(String application, String profile, String label) {
SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
if (hasText(getUsername())) {
svnOperationFactory
@@ -71,8 +68,7 @@ public class SvnKitEnvironmentRepository extends AbstractScmAccessor
else {
checkout(svnOperationFactory);
}
return this.cleaner.clean(loadEnvironment(application, profile, label),
getWorkingDirectory().toURI().toString(), getUri());
return getLocations(label);
}
catch (SVNException e) {
throw new IllegalStateException("Cannot checkout repository", e);
@@ -82,10 +78,7 @@ public class SvnKitEnvironmentRepository extends AbstractScmAccessor
}
}
private synchronized Environment loadEnvironment(String application, String profile,
String label) {
final NativeEnvironmentRepository environmentRepository = new NativeEnvironmentRepository(
getEnvironment());
private String[] getLocations(String label) {
String[] locations = getSearchLocations(getSvnPath(getWorkingDirectory(), label));
boolean exists = false;
for (String location : locations) {
@@ -100,8 +93,7 @@ public class SvnKitEnvironmentRepository extends AbstractScmAccessor
if (!exists) {
throw new NoSuchLabelException("No label found for: " + label);
}
environmentRepository.setSearchLocations(locations);
return environmentRepository.findOne(application, profile, label);
return locations;
}
private void checkout(SvnOperationFactory svnOperationFactory) throws SVNException {

View File

@@ -20,6 +20,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
import org.springframework.cloud.config.server.ConfigServerProperties;
import org.springframework.cloud.config.server.EnvironmentController;
import org.springframework.cloud.config.server.EnvironmentRepository;
import org.springframework.cloud.config.server.ResourceController;
import org.springframework.cloud.config.server.ResourceRepository;
import org.springframework.cloud.config.server.encryption.EnvironmentEncryptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -35,6 +37,9 @@ public class ConfigServerMvcConfiguration {
@Autowired
private EnvironmentRepository repository;
@Autowired
private ResourceRepository resources;
@Autowired
private ConfigServerProperties server;
@@ -43,19 +48,25 @@ public class ConfigServerMvcConfiguration {
@Bean
public EnvironmentController environmentController() {
EnvironmentController controller = new EnvironmentController(repository, environmentEncryptor);
EnvironmentController controller = new EnvironmentController(this.repository, this.environmentEncryptor);
controller.setDefaultLabel(getDefaultLabel());
controller.setOverrides(server.getOverrides());
controller.setStripDocumentFromYaml(server.isStripDocumentFromYaml());
controller.setOverrides(this.server.getOverrides());
controller.setStripDocumentFromYaml(this.server.isStripDocumentFromYaml());
return controller;
}
@Bean
public ResourceController resourceController() {
ResourceController controller = new ResourceController(this.resources, environmentController());
return controller;
}
private String getDefaultLabel() {
if (StringUtils.hasText(server.getDefaultLabel())) {
return server.getDefaultLabel();
if (StringUtils.hasText(this.server.getDefaultLabel())) {
return this.server.getDefaultLabel();
}
else {
return repository.getDefaultLabel();
return this.repository.getDefaultLabel();
}
}
}

View File

@@ -50,11 +50,11 @@ public class EnvironmentRepositoryConfiguration {
protected static class NativeRepositoryConfiguration {
@Autowired
private ConfigurableEnvironment environment;
private ConfigurableEnvironment environment;
@Bean
public EnvironmentRepository environmentRepository() {
return new NativeEnvironmentRepository(environment);
return new NativeEnvironmentRepository(this.environment);
}
}
@@ -62,16 +62,16 @@ public class EnvironmentRepositoryConfiguration {
@Configuration
@ConditionalOnMissingBean(EnvironmentRepository.class)
protected static class GitRepositoryConfiguration {
@Autowired
private ConfigurableEnvironment environment;
private ConfigurableEnvironment environment;
@Bean
public EnvironmentRepository environmentRepository() {
return new MultipleJGitEnvironmentRepository(environment);
return new MultipleJGitEnvironmentRepository(this.environment);
}
}
@Configuration
@Profile("subversion")
protected static class SvnRepositoryConfiguration {
@@ -80,7 +80,7 @@ public class EnvironmentRepositoryConfiguration {
@Bean
public EnvironmentRepository environmentRepository() {
return new SvnKitEnvironmentRepository(environment);
return new SvnKitEnvironmentRepository(this.environment);
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.cloud.config.server.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.config.server.ConfigServerProperties;
import org.springframework.cloud.config.server.GenericResourceRepository;
import org.springframework.cloud.config.server.ResourceLocationService;
import org.springframework.cloud.config.server.ResourceRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Dave Syer
*
*/
@Configuration
@ConditionalOnMissingBean(ResourceRepository.class)
@EnableConfigurationProperties(ConfigServerProperties.class)
public class ResourceRepositoryConfiguration {
@Bean
public ResourceRepository resourceRepository(ResourceLocationService service) {
return new GenericResourceRepository(service);
}
}

View File

@@ -1,12 +1,16 @@
package org.springframework.cloud.config.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyString;
import java.io.IOException;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -20,15 +24,11 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyString;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class)
@IntegrationTest("server.port:0")
@@ -50,27 +50,34 @@ public class ConfigClientOffIntegrationTests {
@Test
public void contextLoads() {
Environment environment = new TestRestTemplate().getForObject("http://localhost:"
+ port + "/foo/development/", Environment.class);
+ this.port + "/foo/development/", Environment.class);
assertTrue(environment.getPropertySources().isEmpty());
}
@Test
public void configClientDisabled() throws Exception {
assertEquals(0, BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
assertEquals(0, BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.context,
ConfigServicePropertySourceLocator.class).length);
}
@Configuration
@Import(ConfigServerApplication.class)
protected static class TestConfiguration {
@Bean
public EnvironmentRepository environmentRepository() {
EnvironmentRepository repository = Mockito.mock(EnvironmentRepository.class);
given(repository.findOne(anyString(), anyString(), anyString())).willReturn(new Environment("", ""));
return repository;
}
@Bean
public ResourceRepository resourceRepository() {
ResourceRepository repository = Mockito.mock(ResourceRepository.class);
given(repository.findOne(anyString(), anyString(), anyString(), anyString())).willReturn(new ByteArrayResource("".getBytes()));
return repository;
}
}
}

View File

@@ -1,12 +1,16 @@
package org.springframework.cloud.config.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyString;
import java.io.IOException;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -20,15 +24,11 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyString;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class)
@IntegrationTest({ "server.port:0", "spring.cloud.config.enabled:true" })
@@ -50,27 +50,34 @@ public class ConfigClientOnIntegrationTests {
@Test
public void contextLoads() {
Environment environment = new TestRestTemplate().getForObject("http://localhost:"
+ port + "/foo/development/", Environment.class);
+ this.port + "/foo/development/", Environment.class);
assertTrue(environment.getPropertySources().isEmpty());
}
@Test
public void configClientEnabled() throws Exception {
assertEquals(1, BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
assertEquals(1, BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.context,
ConfigServicePropertySourceLocator.class).length);
}
@Configuration
@Import(ConfigServerApplication.class)
protected static class TestConfiguration {
@Bean
public EnvironmentRepository environmentRepository() {
EnvironmentRepository repository = Mockito.mock(EnvironmentRepository.class);
given(repository.findOne(anyString(), anyString(), anyString())).willReturn(new Environment("", ""));
return repository;
}
@Bean
public ResourceRepository resourceRepository() {
ResourceRepository repository = Mockito.mock(ResourceRepository.class);
given(repository.findOne(anyString(), anyString(), anyString(), anyString())).willReturn(new ByteArrayResource("".getBytes()));
return repository;
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 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;
import static org.junit.Assert.assertNotNull;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
/**
* @author Dave Syer
*
*/
public class GenericResourceRepositoryTests {
private GenericResourceRepository repository;
private ConfigurableApplicationContext context;
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Before
public void init() {
this.context = new SpringApplicationBuilder(
NativeEnvironmentRepositoryTests.class).web(false).run();
this.repository = new GenericResourceRepository(
new NativeEnvironmentRepository(this.context.getEnvironment()));
this.repository.setResourceLoader(this.context);
this.context.close();
}
@Test
public void locateResource() {
assertNotNull(this.repository.findOne("blah", "default", "master", "foo.properties"));
}
@Test
public void locateProfiledResource() {
assertNotNull(this.repository.findOne("blah", "local", "master", "foo.txt"));
}
@Test(expected=NoSuchResourceException.class)
public void locateMissingResource() {
assertNotNull(this.repository.findOne("blah", "default", "master", "foo.txt"));
}
}

View File

@@ -71,7 +71,7 @@ public class NativeEnvironmentRepositoryTests {
this.repository.setSearchLocations("classpath:/test");
Environment environment = this.repository.findOne("foo", "development", "dev");
assertEquals(3, environment.getPropertySources().size());
// position 1 because it has higher precendence than anything except the
// position 1 because it has higher precedence than anything except the
// foo-development.properties
assertEquals("dev_bar",
environment.getPropertySources().get(1).getSource().get("foo"));

View File

@@ -0,0 +1,72 @@
/*
* Copyright 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;
import static org.junit.Assert.assertEquals;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
/**
* @author Dave Syer
*
*/
public class ResourceControllerTests {
private ResourceController controller;
private GenericResourceRepository repository;
private ConfigurableApplicationContext context;
private NativeEnvironmentRepository environmentRepository;
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Before
public void init() {
this.context = new SpringApplicationBuilder(
NativeEnvironmentRepositoryTests.class).web(false).run();
this.environmentRepository = new NativeEnvironmentRepository(
this.context.getEnvironment());
this.repository = new GenericResourceRepository(this.environmentRepository);
this.repository.setResourceLoader(this.context);
this.controller = new ResourceController(this.repository,
new EnvironmentController(this.environmentRepository));
this.context.close();
}
@Test
public void templateReplacement() throws Exception {
this.environmentRepository.setSearchLocations("classpath:/test");
String resource = this.controller.resolve("foo", "bar", "dev", "template.json");
assertEquals("{\n \"foo\": \"dev_bar\"\n}", resource);
}
@Test
public void escapedPlaceholder() throws Exception {
this.environmentRepository.setSearchLocations("classpath:/test");
String resource = this.controller.resolve("foo", "bar", "dev", "placeholder.txt");
assertEquals("foo: ${foo}", resource);
}
}

View File

@@ -5,7 +5,4 @@ spring:
git:
basedir: target/config
overrides:
spring:
cloud:
config:
enabled: true
spring.cloud.config.enabled: true

View File

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

View File

@@ -0,0 +1 @@
foo: \${foo}

View File

@@ -0,0 +1,3 @@
{
"foo": "${foo}"
}