Merge remote-tracking branch 'origin/2.0.x'
This commit is contained in:
@@ -503,6 +503,14 @@ spring:
|
||||
|
||||
NOTE: The default value for `deleteUntrackedBranches` property is `false`.
|
||||
|
||||
===== Git Refresh Rate
|
||||
|
||||
You can control how often the config server will fetch updated configuration data
|
||||
from your Git backend by using `spring.cloud.config.server.git.refreshRate`. The
|
||||
value of this property is specified in seconds. By default the value is 0, meaning
|
||||
the config server will fetch updated configuration from the Git repo every time it
|
||||
is requested.
|
||||
|
||||
==== Version Control Backend Filesystem Use
|
||||
|
||||
WARNING: With VCS-based backends (git, svn), files are checked out or cloned to the local filesystem.
|
||||
@@ -545,7 +553,7 @@ Vault is a tool for securely accessing secrets.
|
||||
A secret is anything that to which you want to tightly control access, such as API keys, passwords, certificates, and other sensitive information. Vault provides a unified interface to any secret while providing tight access control and recording a detailed audit log.
|
||||
****
|
||||
|
||||
For more information on Vault, see the https://www.vaultproject.io/intro/index.html[Vault quick start guide].
|
||||
For more information on Vault, see the https://learn.hashicorp.com/vault/?track=getting-started#getting-started[Vault quick start guide].
|
||||
|
||||
To enable the config server to use a Vault backend, you can run your config server with the `vault` profile.
|
||||
For example, in your config server's `application.properties`, you can add `spring.profiles.active=vault`.
|
||||
@@ -601,8 +609,8 @@ First, place some data in you Vault, as shown in the following example:
|
||||
|
||||
[source,sh]
|
||||
----
|
||||
$ vault write secret/application foo=bar baz=bam
|
||||
$ vault write secret/myapp foo=myappsbar
|
||||
$ vault kv put secret/application foo=bar baz=bam
|
||||
$ vault kv put secret/myapp foo=myappsbar
|
||||
----
|
||||
|
||||
Second, make an HTTP request to your config server to retrieve the values, as shown in the following example:
|
||||
@@ -963,8 +971,11 @@ The asymmetric choice is superior in terms of security, but it is often more con
|
||||
|
||||
To configure a symmetric key, you need to set `encrypt.key` to a secret String (or use the `ENCRYPT_KEY` environment variable to keep it out of plain-text configuration files).
|
||||
|
||||
To configure an asymmetric key, you can either set the key as a PEM-encoded text value (in `encrypt.key`) or use a keystore (such as the keystore created by the `keytool` utility that comes with the JDK).
|
||||
The following table describes the keystore properties:
|
||||
NOTE: You cannot configure an asymmetric key using `encrypt.key`.
|
||||
|
||||
To configure an asymmetric key use a keystore (e.g. as
|
||||
created by the `keytool` utility that comes with the JDK). The
|
||||
keystore properties are `encrypt.keyStore.\*` with `*` equal to
|
||||
|
||||
[options="header"]
|
||||
|===
|
||||
@@ -1122,6 +1133,24 @@ An optional property named `spring.cloud.config.server.bootstrap` can be useful
|
||||
It is a flag to indicate whether the server should configure itself from its own remote repository.
|
||||
By default, the flag is off, because it can delay startup.
|
||||
However, when embedded in another application, it makes sense to initialize the same way as any other application.
|
||||
When setting `spring.cloud.config.server.bootstrap` to `true` you must also use a <<composite-environment-repositories,composite environment repository configuration>>.
|
||||
For example
|
||||
|
||||
[source,yaml]
|
||||
----
|
||||
spring:
|
||||
application:
|
||||
name: configserver
|
||||
profiles:
|
||||
active: composite
|
||||
cloud:
|
||||
config:
|
||||
server:
|
||||
composite:
|
||||
- type: native
|
||||
search-locations: ${HOME}/Desktop/config
|
||||
bootstrap: true
|
||||
----
|
||||
|
||||
NOTE: If you use the bootstrap flag, the config server needs to have its name and repository URI configured in `bootstrap.yml`.
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.springframework.cloud.config.server.diagnostics;
|
||||
|
||||
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
|
||||
import org.springframework.boot.diagnostics.FailureAnalysis;
|
||||
import org.springframework.cloud.config.server.environment.JGitEnvironmentRepository;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class GitUriFailureAnalyzer extends AbstractFailureAnalyzer<IllegalStateException> {
|
||||
|
||||
public static final String DESCRIPTION = "Invalid config server configuration.";
|
||||
public static final String ACTION = "If you are using the git profile, you need to set a Git URI in your " +
|
||||
"configuration. If you are using a native profile and have spring.cloud.config.server.bootstrap=true, " +
|
||||
"you need to use a composite configuration.";
|
||||
|
||||
@Override
|
||||
protected FailureAnalysis analyze(Throwable rootFailure, IllegalStateException cause) {
|
||||
if(JGitEnvironmentRepository.MESSAGE.equalsIgnoreCase(cause.getMessage())) {
|
||||
return new FailureAnalysis(DESCRIPTION, ACTION, cause);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -80,6 +80,8 @@ import static org.eclipse.jgit.transport.ReceiveCommand.Type.DELETE;
|
||||
public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
|
||||
implements EnvironmentRepository, SearchPathLocator, InitializingBean {
|
||||
|
||||
public static final String MESSAGE = "You need to configure a uri for the git repository.";
|
||||
|
||||
private static final String FILE_URI_PREFIX = "file:";
|
||||
|
||||
private static final String LOCAL_BRANCH_REF_PREFIX = "refs/remotes/origin/";
|
||||
@@ -241,9 +243,9 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
public synchronized void afterPropertiesSet() throws Exception {
|
||||
Assert.state(getUri() != null,
|
||||
"You need to configure a uri for the git repository");
|
||||
MESSAGE);
|
||||
initialize();
|
||||
if (this.cloneOnStart) {
|
||||
initClonedRepository();
|
||||
@@ -572,6 +574,7 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
|
||||
return clone.call();
|
||||
}
|
||||
catch (GitAPIException e) {
|
||||
logger.warn("Error occured cloning to base directory.", e);
|
||||
deleteBaseDirIfExists();
|
||||
throw e;
|
||||
}
|
||||
@@ -683,7 +686,7 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
|
||||
* Wraps the static method calls to {@link org.eclipse.jgit.api.Git} and
|
||||
* {@link org.eclipse.jgit.api.CloneCommand} allowing for easier unit testing.
|
||||
*/
|
||||
static class JGitFactory {
|
||||
public static class JGitFactory {
|
||||
|
||||
public Git getGitByOpen(File file) throws IOException {
|
||||
Git git = Git.open(file);
|
||||
|
||||
@@ -34,7 +34,9 @@ public class NativeEnvironmentRepositoryFactory implements EnvironmentRepository
|
||||
@Override
|
||||
public NativeEnvironmentRepository build(NativeEnvironmentProperties environmentProperties) {
|
||||
NativeEnvironmentRepository repository = new NativeEnvironmentRepository(environment, environmentProperties);
|
||||
repository.setDefaultLabel(properties.getDefaultLabel());
|
||||
if(properties.getDefaultLabel() != null) {
|
||||
repository.setDefaultLabel(properties.getDefaultLabel());
|
||||
}
|
||||
return repository;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.cloud.config.server.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
|
||||
import org.eclipse.jgit.internal.JGitText;
|
||||
@@ -37,6 +38,8 @@ import org.eclipse.jgit.transport.URIish;
|
||||
*/
|
||||
public class GitSkipSslValidationCredentialsProvider extends CredentialsProvider {
|
||||
|
||||
private static final Pattern FORMAT_PLACEHOLDER_PATTERN = Pattern.compile("\\s*\\{\\d}\\s*");
|
||||
|
||||
private final CredentialsProvider delegate;
|
||||
|
||||
public GitSkipSslValidationCredentialsProvider(CredentialsProvider delegate) {
|
||||
@@ -128,6 +131,6 @@ public class GitSkipSslValidationCredentialsProvider extends CredentialsProvider
|
||||
}
|
||||
|
||||
private static String stripFormattingPlaceholders(String string) {
|
||||
return string.replaceAll("\\s*\\{\\d}\\s*", "");
|
||||
return FORMAT_PLACEHOLDER_PATTERN.matcher(string).replaceAll("");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,3 +11,6 @@ org.springframework.cloud.config.server.bootstrap.ConfigServerBootstrapApplicati
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.config.server.config.ConfigServerAutoConfiguration,\
|
||||
org.springframework.cloud.config.server.config.EncryptionAutoConfiguration
|
||||
|
||||
org.springframework.boot.diagnostics.FailureAnalyzer=\
|
||||
org.springframework.cloud.config.server.diagnostics.GitUriFailureAnalyzer
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.springframework.cloud.config.server;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.test.rule.OutputCapture;
|
||||
import org.springframework.cloud.config.server.diagnostics.GitUriFailureAnalyzer;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class NativeBootstrapFailureAnalyzerTests {
|
||||
|
||||
@Rule
|
||||
public OutputCapture outputCapture = new OutputCapture();
|
||||
|
||||
@Test
|
||||
public void contextLoads(){
|
||||
try {
|
||||
new SpringApplicationBuilder(ConfigServerApplication.class)
|
||||
.web(WebApplicationType.SERVLET).properties("spring.cloud.bootstrap.name:enable-nativebootstrap").profiles("test","native").run();
|
||||
fail("Application started successfully");
|
||||
}
|
||||
catch (Exception ex) {
|
||||
assertThat(this.outputCapture.toString())
|
||||
.contains(GitUriFailureAnalyzer.ACTION);
|
||||
assertThat(this.outputCapture.toString()).contains(GitUriFailureAnalyzer.DESCRIPTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,24 @@
|
||||
package org.springframework.cloud.config.server.environment;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.eclipse.jgit.api.CheckoutCommand;
|
||||
import org.eclipse.jgit.api.CloneCommand;
|
||||
import org.eclipse.jgit.api.FetchCommand;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.errors.*;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.transport.FetchResult;
|
||||
import org.eclipse.jgit.util.FileUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -96,6 +108,63 @@ public class JGitEnvironmentRepositoryConcurrencyTests {
|
||||
assertEquals("master", environment.getLabel());
|
||||
}
|
||||
|
||||
protected Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
/**
|
||||
* Simulates following actions in parallel:
|
||||
* - Client tries to obtain configuration with specified label
|
||||
* - Spring Refresh Context Event occurs
|
||||
*/
|
||||
@Test
|
||||
public void concurrentRefreshContextAndGetLabels() throws Exception {
|
||||
// Prepare the repo
|
||||
final JGitConfigServerTestData testData = JGitConfigServerTestData.prepareClonedGitRepository(TestConfiguration.class);
|
||||
JGitEnvironmentRepository repository = testData.getRepository();
|
||||
repository.setCloneOnStart(true);
|
||||
repository.setGitFactory(new DelayedGitFactoryMock());
|
||||
repository.setBasedir(testData.getClonedGit().getGitWorkingDirectory());
|
||||
repository.setUri(testData.getServerGit().getGitWorkingDirectory().getAbsolutePath().replace("file://", ""));
|
||||
|
||||
final AtomicInteger errorCount = new AtomicInteger();
|
||||
|
||||
// Prepare two threads to do the parallel work
|
||||
Thread client = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.info("client start.");
|
||||
try {
|
||||
Environment environment = testData.getRepository().findOne("bar", "staging", "master");
|
||||
} catch (Exception e) {
|
||||
errorCount.incrementAndGet();
|
||||
e.printStackTrace();
|
||||
}
|
||||
logger.info("client end.");
|
||||
}
|
||||
});
|
||||
|
||||
Thread refresh = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
logger.info("refresh start.");
|
||||
testData.getRepository().afterPropertiesSet();
|
||||
logger.info("refresh end.");
|
||||
} catch (Exception e) {
|
||||
errorCount.incrementAndGet();
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Start the parallel actions and wait till the end.
|
||||
refresh.start();
|
||||
client.start();
|
||||
refresh.join();
|
||||
client.join();
|
||||
|
||||
assertEquals(0, errorCount.get());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(ConfigServerProperties.class)
|
||||
@Import({ PropertyPlaceholderAutoConfiguration.class,
|
||||
@@ -103,4 +172,80 @@ public class JGitEnvironmentRepositoryConcurrencyTests {
|
||||
protected static class TestConfiguration {
|
||||
}
|
||||
|
||||
private static class DelayedGitFactoryMock extends JGitEnvironmentRepository.JGitFactory {
|
||||
|
||||
@Override
|
||||
public Git getGitByOpen(File file) throws IOException {
|
||||
Git originalGit = DelayedGitMock.open(file);
|
||||
return new DelayedGitMock(originalGit.getRepository());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloneCommand getCloneCommandByCloneRepository() {
|
||||
return new DelayedCloneCommand();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DelayedGitMock extends Git {
|
||||
|
||||
public DelayedGitMock(Repository repo) {
|
||||
super(repo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchCommand fetch() {
|
||||
return new DelayedFetchCommand(getRepository());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CheckoutCommand checkout() {
|
||||
return new DelayedCheckoutCommand(getRepository());
|
||||
}
|
||||
}
|
||||
|
||||
private static class DelayedCloneCommand extends CloneCommand {
|
||||
@Override
|
||||
public Git call() throws GitAPIException, InvalidRemoteException, TransportException {
|
||||
try {
|
||||
Thread.sleep(250);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return super.call();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DelayedFetchCommand extends FetchCommand {
|
||||
|
||||
public DelayedFetchCommand(Repository repo) {
|
||||
super(repo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchResult call() throws GitAPIException, InvalidRemoteException, TransportException {
|
||||
try {
|
||||
Thread.sleep(250);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return super.call();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DelayedCheckoutCommand extends CheckoutCommand {
|
||||
public DelayedCheckoutCommand(Repository repo) {
|
||||
super(repo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ref call() throws GitAPIException, RefAlreadyExistsException, RefNotFoundException, InvalidRefNameException, CheckoutConflictException {
|
||||
try {
|
||||
Thread.sleep(250);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return super.call();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2018 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.junit.Test;
|
||||
import org.springframework.cloud.config.server.config.ConfigServerProperties;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class NativeEnvironmentRepositoryFactoryTest {
|
||||
|
||||
@Test
|
||||
public void testDefaultLabel() {
|
||||
ConfigServerProperties props = new ConfigServerProperties();
|
||||
props.setDefaultLabel("mylabel");
|
||||
NativeEnvironmentRepositoryFactory factory = new NativeEnvironmentRepositoryFactory(new StandardEnvironment(), props);
|
||||
NativeEnvironmentProperties environmentProperties = new NativeEnvironmentProperties();
|
||||
NativeEnvironmentRepository repo = factory.build(environmentProperties);
|
||||
assertEquals("mylabel", repo.getDefaultLabel());
|
||||
|
||||
factory = new NativeEnvironmentRepositoryFactory(new StandardEnvironment(), props);
|
||||
environmentProperties = new NativeEnvironmentProperties();
|
||||
environmentProperties.setDefaultLabel("mynewlabel");
|
||||
repo = factory.build(environmentProperties);
|
||||
assertEquals("mylabel", repo.getDefaultLabel());
|
||||
|
||||
factory = new NativeEnvironmentRepositoryFactory(new StandardEnvironment(), new ConfigServerProperties());
|
||||
environmentProperties = new NativeEnvironmentProperties();
|
||||
environmentProperties.setDefaultLabel("mynewlabel");
|
||||
repo = factory.build(environmentProperties);
|
||||
assertEquals("mynewlabel", repo.getDefaultLabel());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
spring:
|
||||
cloud:
|
||||
config:
|
||||
server:
|
||||
bootstrap: true
|
||||
Reference in New Issue
Block a user