diff --git a/docs/src/main/asciidoc/spring-cloud-config.adoc b/docs/src/main/asciidoc/spring-cloud-config.adoc index 9bf6682f..922ce9a6 100644 --- a/docs/src/main/asciidoc/spring-cloud-config.adoc +++ b/docs/src/main/asciidoc/spring-cloud-config.adoc @@ -556,6 +556,58 @@ $ vault write secret/application foo=bar baz=bam All applications using the config server will have the properties `foo` and `baz` available to them. +==== Composite Environment Repositories + +In some scenarios you may wish to pull configuration data from multiple +environment repositories. To do this just enable +multiple profiles in your config server's application properties or YAML file. +If, for example, you want to pull configuration data from a Git repository +as well as a SVN repository you would set the following properties for your +configuration server. + +[source,yaml] +---- +spring: + profiles: + active: git, svn + cloud: + config: + server: + svn: + uri: file:///path/to/svn/repo + order: 2 + git: + uri: file:///path/to/git/repo + order: 1 +---- + +In addition to each repo specifying a URI, you can also specify an `order` property. +The `order` property allows you to specify the priority order for all your repositories. +The lower the numerical value of the `order` property the higher priority it will have. +The priority order of a repository will help resolve any potential conflicts between +repositories that contain values for the same properties. + +NOTE: Any type of failure when retrieving values from an environment repositoy +will result in a failure for the entire composite environment. + +NOTE: When using a composite environment it is important that all repos contain +the same label(s). If you have an environment similar to the one above and you request +configuration data with the label `master` but the SVN +repo does not contain a branch called `master` the entire request will fail. + +===== Custom Composite Environment Repositories + +It is also possible to provide your own `EnvironmentRepository` bean +to be included as part of a composite environment in addition to +using one of the environment repositories from Spring Cloud. To do this your bean +must implement `OrderedEnvironmentRepository`. For convenience sake, there is +an abstract implementation of this interface you can extend called +`AbstractOrderedEnvironmentRepository`. If you extend `AbstractOrderedEnvironmentRepository` +your environment repository will have the lowest possible precedence by default. +If you would like to change this, make sure you call the `setOrder` method +to set the precedence or override the `getOrder` method to return the order +value you would like your environment repository to have. + ==== Property Overrides The Config Server has an "overrides" feature that allows the operator diff --git a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/environment/Environment.java b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/environment/Environment.java index c8794f7b..e60e9dcd 100644 --- a/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/environment/Environment.java +++ b/spring-cloud-config-client/src/main/java/org/springframework/cloud/config/environment/Environment.java @@ -76,6 +76,10 @@ public class Environment { this.propertySources.add(propertySource); } + public void add(List propertySources) { + this.propertySources.addAll(propertySources); + } + public void addFirst(PropertySource propertySource) { this.propertySources.add(0, propertySource); } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/CompositeConfiguration.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/CompositeConfiguration.java new file mode 100644 index 00000000..21909285 --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/CompositeConfiguration.java @@ -0,0 +1,59 @@ +/* + * Copyright 2013-2016 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 java.util.ArrayList; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.config.server.environment.CompositeEnvironmentRepository; +import org.springframework.cloud.config.server.environment.OrderedEnvironmentRepository; +import org.springframework.cloud.config.server.environment.SearchPathCompositeEnvironmentRepository; +import org.springframework.cloud.config.server.environment.SearchPathLocator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +/** + * @author Ryan Baxter + */ +@Configuration +@ConditionalOnBean(OrderedEnvironmentRepository.class) +public class CompositeConfiguration { + + private List environmentRepos = new ArrayList<>(); + + @Bean + @Primary + @ConditionalOnBean(SearchPathLocator.class) + public SearchPathCompositeEnvironmentRepository compositeEnvironmentRepository() { + return new SearchPathCompositeEnvironmentRepository(environmentRepos); + } + + @Bean + @Primary + @ConditionalOnMissingBean(SearchPathLocator.class) + public CompositeEnvironmentRepository compositeEnvironmentRepository2() { + return new CompositeEnvironmentRepository(environmentRepos); + } + + @Autowired + private void setEnvironmentRepos(List repos) { + this.environmentRepos = repos; + } + +} diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerAutoConfiguration.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerAutoConfiguration.java index 3917c2a4..b1dff1ca 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerAutoConfiguration.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/ConfigServerAutoConfiguration.java @@ -28,7 +28,7 @@ import org.springframework.context.annotation.Import; @Configuration @ConditionalOnBean(ConfigServerConfiguration.Marker.class) @EnableConfigurationProperties(ConfigServerProperties.class) -@Import({ EnvironmentRepositoryConfiguration.class, ResourceRepositoryConfiguration.class, +@Import({ EnvironmentRepositoryConfiguration.class, CompositeConfiguration.class, ResourceRepositoryConfiguration.class, ConfigServerEncryptionConfiguration.class, ConfigServerMvcConfiguration.class }) public class ConfigServerAutoConfiguration { diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration.java index af263c13..d333c971 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/config/EnvironmentRepositoryConfiguration.java @@ -20,7 +20,6 @@ import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.config.server.environment.ConsulEnvironmentWatch; import org.springframework.cloud.config.server.environment.EnvironmentRepository; import org.springframework.cloud.config.server.environment.EnvironmentWatch; @@ -36,10 +35,10 @@ import org.springframework.web.client.RestTemplate; /** * @author Dave Syer + * @author Ryan Baxter * */ @Configuration -@ConditionalOnMissingBean(EnvironmentRepository.class) public class EnvironmentRepositoryConfiguration { @Bean @@ -56,15 +55,14 @@ public class EnvironmentRepositoryConfiguration { private ConfigurableEnvironment environment; @Bean - public NativeEnvironmentRepository environmentRepository() { + public NativeEnvironmentRepository nativeEnvironmentRepository() { return new NativeEnvironmentRepository(this.environment); } - } @Configuration @ConditionalOnMissingBean(EnvironmentRepository.class) - protected static class GitRepositoryConfiguration { + protected static class DefaultRepositoryConfiguration { @Autowired private ConfigurableEnvironment environment; @@ -73,7 +71,7 @@ public class EnvironmentRepositoryConfiguration { private ConfigServerProperties server; @Bean - public MultipleJGitEnvironmentRepository environmentRepository() { + public MultipleJGitEnvironmentRepository defaultEnvironmentRepository() { MultipleJGitEnvironmentRepository repository = new MultipleJGitEnvironmentRepository(this.environment); if (this.server.getDefaultLabel()!=null) { repository.setDefaultLabel(this.server.getDefaultLabel()); @@ -82,6 +80,10 @@ public class EnvironmentRepositoryConfiguration { } } + @Configuration + @Profile("git") + protected static class GitRepositoryConfiguration extends DefaultRepositoryConfiguration {} + @Configuration @Profile("subversion") protected static class SvnRepositoryConfiguration { @@ -92,7 +94,7 @@ public class EnvironmentRepositoryConfiguration { private ConfigServerProperties server; @Bean - public SvnKitEnvironmentRepository environmentRepository() { + public SvnKitEnvironmentRepository svnKitEnvironmentRepository() { SvnKitEnvironmentRepository repository = new SvnKitEnvironmentRepository(this.environment); if (this.server.getDefaultLabel()!=null) { repository.setDefaultLabel(this.server.getDefaultLabel()); @@ -105,7 +107,7 @@ public class EnvironmentRepositoryConfiguration { @Profile("vault") protected static class VaultConfiguration { @Bean - public EnvironmentRepository environmentRepository(HttpServletRequest request, EnvironmentWatch watch) { + public VaultEnvironmentRepository valutEnvironmentRepository(HttpServletRequest request, EnvironmentWatch watch) { return new VaultEnvironmentRepository(request, watch, new RestTemplate()); } } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/AbstractOrderedEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/AbstractOrderedEnvironmentRepository.java new file mode 100644 index 00000000..9238e5e0 --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/AbstractOrderedEnvironmentRepository.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2016 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 org.springframework.core.Ordered; + +/** + * @author Ryan Baxter + */ +public abstract class AbstractOrderedEnvironmentRepository implements OrderedEnvironmentRepository { + private int order = Ordered.LOWEST_PRECEDENCE; + + @Override + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } +} diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/AbstractScmEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/AbstractScmEnvironmentRepository.java index 454d63b3..1a89097d 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/AbstractScmEnvironmentRepository.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/AbstractScmEnvironmentRepository.java @@ -18,6 +18,7 @@ package org.springframework.cloud.config.server.environment; import org.springframework.cloud.config.environment.Environment; import org.springframework.cloud.config.server.support.AbstractScmAccessor; +import org.springframework.core.Ordered; import org.springframework.core.env.ConfigurableEnvironment; /** @@ -25,9 +26,10 @@ import org.springframework.core.env.ConfigurableEnvironment; * */ public abstract class AbstractScmEnvironmentRepository extends AbstractScmAccessor - implements EnvironmentRepository, SearchPathLocator { + implements OrderedEnvironmentRepository, SearchPathLocator { private EnvironmentCleaner cleaner = new EnvironmentCleaner(); + private int order = Ordered.LOWEST_PRECEDENCE; public AbstractScmEnvironmentRepository(ConfigurableEnvironment environment) { super(environment); @@ -46,4 +48,12 @@ public abstract class AbstractScmEnvironmentRepository extends AbstractScmAccess getUri()); } + @Override + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/CompositeEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/CompositeEnvironmentRepository.java new file mode 100644 index 00000000..1ce6faae --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/CompositeEnvironmentRepository.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2016 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 java.util.Collections; +import java.util.List; +import org.springframework.cloud.config.environment.Environment; +import org.springframework.core.OrderComparator; + +/** + * An {@link EnvironmentRepository} composed of multiple ordered {@link OrderedEnvironmentRepository}s. + * @author Ryan Baxter + */ +public class CompositeEnvironmentRepository implements EnvironmentRepository { + + protected List environmentRepositories; + + /** + * Creates a new {@link CompositeEnvironmentRepository}. + * @param environmentRepositories The list of {@link OrderedEnvironmentRepository}s to create the composite from. + */ + public CompositeEnvironmentRepository(List environmentRepositories) { + //Sort the environment repositories by the priority + Collections.sort(environmentRepositories, OrderComparator.INSTANCE); + this.environmentRepositories = environmentRepositories; + } + + @Override + public Environment findOne(String application, String profile, String label) { + Environment env = new Environment(application, new String[]{profile}, label, null, null); + for(EnvironmentRepository repo : environmentRepositories) { + env.add(repo.findOne(application, profile, label).getPropertySources()); + } + return env; + } +} 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 1d278f3f..b5333be5 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 @@ -293,4 +293,8 @@ public class MultipleJGitEnvironmentRepository extends JGitEnvironmentRepository } + @Override + public void setOrder(int order) { + super.setOrder(order); + } } 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 83f7c494..0ebb9507 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 @@ -47,8 +47,8 @@ import org.springframework.util.StringUtils; * @author Roy Clarkson */ @ConfigurationProperties("spring.cloud.config.server.native") -public class NativeEnvironmentRepository - implements EnvironmentRepository, SearchPathLocator { +public class NativeEnvironmentRepository extends AbstractOrderedEnvironmentRepository + implements SearchPathLocator { private static Log logger = LogFactory.getLog(NativeEnvironmentRepository.class); diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/OrderedEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/OrderedEnvironmentRepository.java new file mode 100644 index 00000000..033580d4 --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/OrderedEnvironmentRepository.java @@ -0,0 +1,25 @@ +/* + * Copyright 2013-2016 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 org.springframework.core.Ordered; + +/** + * A prioritized {@link EnvironmentRepository}. + * @author Ryan Baxter + */ +public interface OrderedEnvironmentRepository extends EnvironmentRepository, Ordered { +} diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/SearchPathCompositeEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/SearchPathCompositeEnvironmentRepository.java new file mode 100644 index 00000000..1ee23fee --- /dev/null +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/SearchPathCompositeEnvironmentRepository.java @@ -0,0 +1,46 @@ +/* + * Copyright 2013-2016 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 java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A {@link CompositeEnvironmentRepository} which implements {@link SearchPathLocator}. + * @author Ryan Baxter + */ +public class SearchPathCompositeEnvironmentRepository extends CompositeEnvironmentRepository implements SearchPathLocator { + + /** + * Creates a new {@link SearchPathCompositeEnvironmentRepository}. + * @param environmentRepositories The {@link OrderedEnvironmentRepository}s to create this composite from. + */ + public SearchPathCompositeEnvironmentRepository(List environmentRepositories) { + super(environmentRepositories); + } + + @Override + public Locations getLocations(String application, String profile, String label) { + List locations = new ArrayList<>(); + for(EnvironmentRepository repo : this.environmentRepositories) { + if(repo instanceof SearchPathLocator) { + locations.addAll(Arrays.asList(((SearchPathLocator) repo).getLocations(application, profile, label).getLocations())); + } + } + return new Locations(application, profile, label, null, locations.toArray(new String[locations.size()])); + } +} diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/SvnKitEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/SvnKitEnvironmentRepository.java index 63707ba0..af19b126 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/SvnKitEnvironmentRepository.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/SvnKitEnvironmentRepository.java @@ -64,6 +64,10 @@ public class SvnKitEnvironmentRepository extends AbstractScmEnvironmentRepositor this.defaultLabel = defaultLabel; } + public SvnKitEnvironmentRepository(ConfigurableEnvironment environment) { + super(environment); + } + @Override public synchronized Locations getLocations(String application, String profile, String label) { @@ -168,10 +172,6 @@ public class SvnKitEnvironmentRepository extends AbstractScmEnvironmentRepositor } - public SvnKitEnvironmentRepository(ConfigurableEnvironment environment) { - super(environment); - } - @Override protected File getWorkingDirectory() { return this.getBasedir(); @@ -193,4 +193,9 @@ public class SvnKitEnvironmentRepository extends AbstractScmEnvironmentRepositor return svnPath; } + @Override + public void setOrder(int order) { + super.setOrder(order); + } + } diff --git a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepository.java b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepository.java index e7e5290a..e68b26a7 100644 --- a/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepository.java +++ b/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/VaultEnvironmentRepository.java @@ -35,7 +35,7 @@ import static org.springframework.cloud.config.client.ConfigClientProperties.TOK * @author Spencer Gibb */ @ConfigurationProperties("spring.cloud.config.server.vault") -public class VaultEnvironmentRepository implements EnvironmentRepository { +public class VaultEnvironmentRepository extends AbstractOrderedEnvironmentRepository { public static final String VAULT_TOKEN = "X-Vault-Token"; @@ -190,6 +190,11 @@ public class VaultEnvironmentRepository implements EnvironmentRepository { this.profileSeparator = profileSeparator; } + @Override + public void setOrder(int order) { + super.setOrder(order); + } + static class VaultResponse { private String auth; diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/CompositeConfigServerIntegrationTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/CompositeConfigServerIntegrationTests.java new file mode 100644 index 00000000..d1b99b1d --- /dev/null +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/CompositeConfigServerIntegrationTests.java @@ -0,0 +1,87 @@ +/* + * Copyright 2013-2016 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.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.context.embedded.LocalServerPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.cloud.config.environment.Environment; +import org.springframework.cloud.config.server.test.ConfigServerTestUtils; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +/** + * @author Ryan Baxter + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = ConfigServerApplication.class, + properties = { "spring.config.name:compositeconfigserver", + "spring.cloud.config.server.svn.uri:file:///./target/repos/svn-config-repo", + "spring.cloud.config.server.svn.order:2", + "spring.cloud.config.server.git.uri:file:./target/repos/config-repo", + "spring.cloud.config.server.git.order:1"}, + webEnvironment = RANDOM_PORT) +@ActiveProfiles({ "test", "git", "subversion" }) +public class CompositeConfigServerIntegrationTests { + + @LocalServerPort + private int port; + + + + + @BeforeClass + public static void init() throws Exception { + ConfigServerTestUtils.prepareLocalRepo(); + ConfigServerTestUtils.prepareLocalSvnRepo("src/test/resources/svn-config-repo", + "target/repos/svn-config-repo"); + } + + @Test + public void contextLoads() { + Environment environment = new TestRestTemplate().getForObject("http://localhost:" + + port + "/foo/development/", Environment.class); + assertEquals(3, environment.getPropertySources().size()); + assertEquals("overrides", environment.getPropertySources().get(0).getName()); + assertTrue(environment.getPropertySources().get(1).getName().contains("config-repo") && + !environment.getPropertySources().get(1).getName().contains("svn-config-repo")); + assertTrue(environment.getPropertySources().get(2).getName().contains("svn-config-repo")); + assertEquals("{spring.cloud.config.enabled=true}", environment + .getPropertySources().get(0).getSource().toString()); + } + + @Test + public void resourseEndpointsWork() { + //This request will get the file from the Git Repo + String text = new TestRestTemplate().getForObject("http://localhost:" + + port + "/foo/development/composite/bar.properties", String.class); + + String expected = "foo: bar"; + assertEquals("invalid content", expected, text); + + //This request will get the file from the SVN Repo + text = new TestRestTemplate().getForObject("http://localhost:" + + port + "/foo/development/composite/bar.properties", String.class); + assertEquals("invalid content", expected, text); + } +} diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/NativeConfigServerIntegrationTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/NativeConfigServerIntegrationTests.java index a80d21bf..084fdee0 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/NativeConfigServerIntegrationTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/NativeConfigServerIntegrationTests.java @@ -49,10 +49,4 @@ public class NativeConfigServerIntegrationTests { + port + "/bad/default/", String.class); assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); } - - public static void main(String[] args) { - new SpringApplicationBuilder(ConfigServerApplication.class).profiles("native").properties( - "spring.config.name=configserver").run(args); - } - } diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/config/CustomCompositeEnvironmentRepositoryTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/config/CustomCompositeEnvironmentRepositoryTests.java new file mode 100644 index 00000000..fc038cb1 --- /dev/null +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/config/CustomCompositeEnvironmentRepositoryTests.java @@ -0,0 +1,100 @@ +/* + * Copyright 2013-2016 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 java.util.HashMap; +import java.util.List; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.context.embedded.LocalServerPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.cloud.config.environment.Environment; +import org.springframework.cloud.config.environment.PropertySource; +import org.springframework.cloud.config.server.EnableConfigServer; +import org.springframework.cloud.config.server.environment.AbstractOrderedEnvironmentRepository; +import org.springframework.cloud.config.server.environment.OrderedEnvironmentRepository; +import org.springframework.cloud.config.server.test.ConfigServerTestUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author Ryan Baxter + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = CustomCompositeEnvironmentRepositoryTests.TestApplication.class, properties = { + "spring.config.name:compositeconfigserver", "spring.cloud.config.server.git.uri:file:./target/repos/config-repo", + "spring.cloud.config.server.git.order:1" }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ActiveProfiles({"test", "git"}) +@DirtiesContext +public class CustomCompositeEnvironmentRepositoryTests { + + @LocalServerPort + private int port; + + @BeforeClass + public static void init() throws Exception { + ConfigServerTestUtils.prepareLocalRepo(); + } + + @Test + public void contextLoads() { + Environment environment = new TestRestTemplate().getForObject( + "http://localhost:" + port + "/foo/development/", Environment.class); + List propertySources = environment.getPropertySources(); + assertEquals(3, propertySources.size()); + assertEquals("overrides", propertySources.get(0).getName()); + assertTrue(propertySources.get(1).getName().contains("config-repo")); + assertEquals("p", propertySources.get(2).getName()); + } + + @Configuration + @EnableAutoConfiguration + @EnableConfigServer + protected static class TestApplication { + + @Bean + public OrderedEnvironmentRepository environmentRepository() { + return new AbstractOrderedEnvironmentRepository() { + + @Override + public Environment findOne(String application, String profile, + String label) { + Environment e = new Environment("test", new String[0], "label", "version", + "state"); + PropertySource p = new PropertySource("p", new HashMap<>()); + e.add(p); + return e; + } + }; + } + + public static void main(String[] args) throws Exception { + SpringApplication.run(CustomEnvironmentRepositoryTests.TestApplication.class, + args); + } + + } +} diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/CompositeEnvironmentRepositoryTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/CompositeEnvironmentRepositoryTests.java new file mode 100644 index 00000000..73f9b93f --- /dev/null +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/CompositeEnvironmentRepositoryTests.java @@ -0,0 +1,106 @@ +/* + * Copyright 2013-2016 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 java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import org.springframework.cloud.config.environment.Environment; +import org.springframework.cloud.config.environment.PropertySource; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +/** + * @author Ryan Baxter + */ +public class CompositeEnvironmentRepositoryTests { + + private class TestOrderedEnvironmentRepository extends AbstractOrderedEnvironmentRepository implements SearchPathLocator { + + private Environment env; + private Locations locations; + + public TestOrderedEnvironmentRepository(int order, Environment env, Locations locations) { + setOrder(order); + this.env = env; + this.locations = locations; + } + + @Override + public Environment findOne(String application, String profile, String label) { + return env; + } + + @Override + public Locations getLocations(String application, String profile, String label) { + return locations; + } + } + + @Test + public void testOrder() { + PropertySource p1 = mock(PropertySource.class); + doReturn("p1").when(p1).getName(); + PropertySource p2 = mock(PropertySource.class); + doReturn("p2").when(p2).getName(); + PropertySource p3 = mock(PropertySource.class); + doReturn("p3").when(p3).getName(); + PropertySource p4 = mock(PropertySource.class); + doReturn("p4").when(p4).getName(); + PropertySource p5 = mock(PropertySource.class); + doReturn("p5").when(p5).getName(); + String sLoc1 = "loc1"; + String sLoc2 = "loc2"; + String sLoc3 = "loc3"; + String sLoc4 = "loc4"; + String sLoc5 = "loc5"; + Environment e1 = new Environment("app", "dev"); + e1.add(p1); + e1.add(p5); + Environment e2 = new Environment("app", "dev"); + e2.add(p2); + Environment e3 = new Environment("app", "dev"); + e3.add(p3); + e3.add(p4); + SearchPathLocator.Locations loc1 = new SearchPathLocator.Locations("app", "dev", "label", "version", new String[]{sLoc1}); + SearchPathLocator.Locations loc2 = new SearchPathLocator.Locations("app", "dev", "label", "version", new String[]{sLoc5, sLoc4}); + SearchPathLocator.Locations loc3 = new SearchPathLocator.Locations("app", "dev", "label", "version", new String[]{sLoc3, sLoc2}); + List repos = new ArrayList(); + repos.add(new TestOrderedEnvironmentRepository(3, e1, loc1)); + repos.add(new TestOrderedEnvironmentRepository(2, e3, loc2)); + repos.add(new TestOrderedEnvironmentRepository(1, e2, loc3)); + SearchPathCompositeEnvironmentRepository compositeRepo = new SearchPathCompositeEnvironmentRepository(repos); + Environment compositeEnv = compositeRepo.findOne("foo", "bar", "world"); + List propertySources = compositeEnv.getPropertySources(); + assertEquals(5, propertySources.size()); + assertEquals("p2", propertySources.get(0).getName()); + assertEquals("p3", propertySources.get(1).getName()); + assertEquals("p4", propertySources.get(2).getName()); + assertEquals("p1", propertySources.get(3).getName()); + assertEquals("p5", propertySources.get(4).getName()); + + SearchPathLocator.Locations locations = compositeRepo.getLocations("app", "dev", "label"); + String[] locationStrings = locations.getLocations(); + assertEquals(5, locationStrings.length); + assertEquals(sLoc3, locationStrings[0]); + assertEquals(sLoc2, locationStrings[1]); + assertEquals(sLoc5, locationStrings[2]); + assertEquals(sLoc4, locationStrings[3]); + assertEquals(sLoc1, locationStrings[4]); + } +} diff --git a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepositoryIntegrationTests.java b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepositoryIntegrationTests.java index 6f1fde9d..27834415 100644 --- a/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepositoryIntegrationTests.java +++ b/spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepositoryIntegrationTests.java @@ -273,7 +273,7 @@ public class JGitEnvironmentRepositoryIntegrationTests { .run("--spring.cloud.config.server.git.uri=" + uri, "--spring.cloud.config.server.git.cloneOnStart=true"); EnvironmentRepository repository = this.context - .getBean(EnvironmentRepository.class); + .getBean(JGitEnvironmentRepository.class); assertTrue(((JGitEnvironmentRepository) repository).isCloneOnStart()); Environment environment = repository.findOne("bar", "staging", "master"); assertEquals(2, environment.getPropertySources().size()); diff --git a/spring-cloud-config-server/src/test/resources/compositeconfigserver.yml b/spring-cloud-config-server/src/test/resources/compositeconfigserver.yml new file mode 100644 index 00000000..65e1b217 --- /dev/null +++ b/spring-cloud-config-server/src/test/resources/compositeconfigserver.yml @@ -0,0 +1,9 @@ +spring: + cloud: + config: + server: + overrides: + spring: + cloud: + config: + enabled: true \ No newline at end of file diff --git a/spring-cloud-config-server/src/test/resources/config-repo/git/index b/spring-cloud-config-server/src/test/resources/config-repo/git/index index 4874f293..95bd7dda 100644 Binary files a/spring-cloud-config-server/src/test/resources/config-repo/git/index and b/spring-cloud-config-server/src/test/resources/config-repo/git/index differ diff --git a/spring-cloud-config-server/src/test/resources/config-repo/git/logs/HEAD b/spring-cloud-config-server/src/test/resources/config-repo/git/logs/HEAD index c6423ee9..9e452d3e 100644 --- a/spring-cloud-config-server/src/test/resources/config-repo/git/logs/HEAD +++ b/spring-cloud-config-server/src/test/resources/config-repo/git/logs/HEAD @@ -1 +1,5 @@ 0000000000000000000000000000000000000000 7df4a26d5437d9d4090cd5809967f870444cde8f Dave Syer 1406860717 -0700 +7df4a26d5437d9d4090cd5809967f870444cde8f 9f01fb972bc9617e4ea59f5c8ee3ceb5ff515cd0 Ryan Baxter 1481905383 -0500 checkout: moving from raw to master +9f01fb972bc9617e4ea59f5c8ee3ceb5ff515cd0 7df4a26d5437d9d4090cd5809967f870444cde8f Ryan Baxter 1481905407 -0500 checkout: moving from master to raw +7df4a26d5437d9d4090cd5809967f870444cde8f 7df4a26d5437d9d4090cd5809967f870444cde8f Ryan Baxter 1481905544 -0500 checkout: moving from raw to composite +7df4a26d5437d9d4090cd5809967f870444cde8f 7df4a26d5437d9d4090cd5809967f870444cde8f Ryan Baxter 1481905552 -0500 checkout: moving from composite to raw diff --git a/spring-cloud-config-server/src/test/resources/config-repo/git/logs/refs/heads/composite b/spring-cloud-config-server/src/test/resources/config-repo/git/logs/refs/heads/composite new file mode 100644 index 00000000..67ab67c5 --- /dev/null +++ b/spring-cloud-config-server/src/test/resources/config-repo/git/logs/refs/heads/composite @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 7df4a26d5437d9d4090cd5809967f870444cde8f Ryan Baxter 1481905474 -0500 branch: Created from raw diff --git a/spring-cloud-config-server/src/test/resources/config-repo/git/refs/heads/composite b/spring-cloud-config-server/src/test/resources/config-repo/git/refs/heads/composite new file mode 100644 index 00000000..4389eb3a --- /dev/null +++ b/spring-cloud-config-server/src/test/resources/config-repo/git/refs/heads/composite @@ -0,0 +1 @@ +7df4a26d5437d9d4090cd5809967f870444cde8f diff --git a/spring-cloud-config-server/src/test/resources/svn-config-repo/.svn/wc.db b/spring-cloud-config-server/src/test/resources/svn-config-repo/.svn/wc.db index 654ccf58..318e177c 100644 Binary files a/spring-cloud-config-server/src/test/resources/svn-config-repo/.svn/wc.db and b/spring-cloud-config-server/src/test/resources/svn-config-repo/.svn/wc.db differ diff --git a/spring-cloud-config-server/src/test/resources/svn-config-repo/.svn/wc.db-journal b/spring-cloud-config-server/src/test/resources/svn-config-repo/.svn/wc.db-journal new file mode 100644 index 00000000..e69de29b diff --git a/spring-cloud-config-server/src/test/resources/svn-config-repo/db/current b/spring-cloud-config-server/src/test/resources/svn-config-repo/db/current index 0cfbf088..00750edc 100644 --- a/spring-cloud-config-server/src/test/resources/svn-config-repo/db/current +++ b/spring-cloud-config-server/src/test/resources/svn-config-repo/db/current @@ -1 +1 @@ -2 +3 diff --git a/spring-cloud-config-server/src/test/resources/svn-config-repo/db/revprops/0/3 b/spring-cloud-config-server/src/test/resources/svn-config-repo/db/revprops/0/3 new file mode 100644 index 00000000..7629ee67 --- /dev/null +++ b/spring-cloud-config-server/src/test/resources/svn-config-repo/db/revprops/0/3 @@ -0,0 +1,13 @@ +K 10 +svn:author +V 11 +ryanjbaxter +K 8 +svn:date +V 27 +2016-12-16T16:51:59.468533Z +K 7 +svn:log +V 23 +adding composite branch +END diff --git a/spring-cloud-config-server/src/test/resources/svn-config-repo/db/revs/0/3 b/spring-cloud-config-server/src/test/resources/svn-config-repo/db/revs/0/3 new file mode 100644 index 00000000..afa7e38e Binary files /dev/null and b/spring-cloud-config-server/src/test/resources/svn-config-repo/db/revs/0/3 differ diff --git a/spring-cloud-config-server/src/test/resources/svn-config-repo/db/transactions/2-4.txn/node.0.0 b/spring-cloud-config-server/src/test/resources/svn-config-repo/db/transactions/2-4.txn/node.0.0 new file mode 100644 index 00000000..c9d9c47a --- /dev/null +++ b/spring-cloud-config-server/src/test/resources/svn-config-repo/db/transactions/2-4.txn/node.0.0 @@ -0,0 +1,9 @@ +id: 0.0.t2-4 +type: dir +pred: 0.0.r2/1211 +count: 3 +text: 2 1126 72 0 a8927d430ec09518c534d157c6e49580 +cpath: / +copyroot: 0 / +is-fresh-txn-root: y + diff --git a/spring-cloud-config-server/src/test/resources/svn-config-repo/db/txn-current b/spring-cloud-config-server/src/test/resources/svn-config-repo/db/txn-current index 0cfbf088..1e8b3149 100644 --- a/spring-cloud-config-server/src/test/resources/svn-config-repo/db/txn-current +++ b/spring-cloud-config-server/src/test/resources/svn-config-repo/db/txn-current @@ -1 +1 @@ -2 +6