Use local file:/... repository in place instead of cloning

Since JGit cannot use the "local" protocol (only git, ssh and https) we can't
use file:/... repositories in the same way as the other protocols. Formerly
we would copy the whole directory and use the copy as a local cache. But then
users are confused when they commit changes to the "remote" and they don't
show up in the server. It seems less confusing to simply use the local
repository as the basedir (although it does mean that ssh: should be used
if you are scaling up the config server).

Fixes gh-63
This commit is contained in:
Dave Syer
2015-01-19 15:05:21 +00:00
parent 048bc6a9a7
commit fecc7c8539
5 changed files with 72 additions and 19 deletions

View File

@@ -72,11 +72,15 @@ the repository you can set the "spring.cloud.config.server.git.uri"
configuration property in the Config Server (e.g. in
`application.yml`). If you set it with a `file:` prefix it should work
from a local repository so you can get started quickly and easily
without a server (it doesn't matter if it's not bare because the
Config Server never makes changes to the "remote" repository). To
scale the Config Server up and make it highly available, however, you
would need to have all instances of the server pointing to the same
repository, so only a shared file system would work.
without a server, but in that case the server operates directly on the
local repository without cloning it (it doesn't matter if it's not
bare because the Config Server never makes changes to the "remote"
repository). To scale the Config Server up and make it highly
available, you would need to have all instances of the server pointing
to the same repository, so only a shared file system would work. Even
in that case it is better to use the `ssh:` protocol for a shared
filesystem repository, so that the server can clone it and use a local
working copy as a cache.
This repository implementation maps the `{label}` parameter of the
HTTP resource to a git label (commit id, branch name or tag).

View File

@@ -19,6 +19,7 @@ import java.io.File;
import java.io.IOException;
import org.eclipse.jgit.util.FileUtils;
import org.springframework.util.FileSystemUtils;
/**
* @author Dave Syer
@@ -27,21 +28,24 @@ import org.eclipse.jgit.util.FileUtils;
public class ConfigServerTestUtils {
public static String prepareLocalRepo() throws IOException {
return prepareLocalRepo("target/test-classes", "config-repo", "target/config");
return prepareLocalRepo("target/repos", "config-repo", "target/config");
}
public static String prepareLocalRepo(String repoPath) throws IOException {
return prepareLocalRepo("target/test-classes", repoPath, "target/config");
return prepareLocalRepo("target/repos", repoPath, "target/config");
}
public static String prepareLocalRepo(String buildDir, String repoPath,
String checkoutDir) throws IOException {
new File(buildDir).mkdirs();
if (!repoPath.startsWith("/")) {
repoPath = "/" + repoPath;
}
if (!repoPath.endsWith("/")) {
repoPath = repoPath + "/";
}
File source = new File("src/test/resources" + repoPath);
FileSystemUtils.copyRecursively(source, new File(buildDir + repoPath));
File dotGit = new File(buildDir + repoPath + ".git");
File git = new File(buildDir + repoPath + "git");
if (git.exists()) {
@@ -60,4 +64,16 @@ public class ConfigServerTestUtils {
return "file:" + buildDir + repoPath;
}
public static String copyLocalRepo(String path) throws IOException {
File dest = new File("target/repos/" + path);
FileSystemUtils.deleteRecursively(dest);
FileSystemUtils.copyRecursively(new File("target/repos/config-repo"), dest);
return "file:./target/repos/" + path;
}
public static boolean deleteLocalRepo(String path) throws IOException {
File dest = new File("target/repos/" + path);
return FileSystemUtils.deleteRecursively(dest);
}
}

View File

@@ -49,7 +49,7 @@ import org.springframework.cloud.config.PropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.UrlResource;
import org.springframework.util.Assert;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.StringUtils;
import com.jcraft.jsch.Session;
@@ -75,10 +75,10 @@ public class JGitEnvironmentRepository implements EnvironmentRepository, Initial
private boolean initialized;
private String[] searchPaths = new String[0];
@Override
public void afterPropertiesSet() throws Exception {
Assert.state(uri!=null, "You need to configure a uri for the git repository");
Assert.state(uri != null, "You need to configure a uri for the git repository");
}
public JGitEnvironmentRepository(ConfigurableEnvironment environment) {
@@ -220,8 +220,8 @@ public class JGitEnvironmentRepository implements EnvironmentRepository, Initial
* Assumes we are on a tracking branch (should be safe)
*/
private void pull(Git git, String label, Ref ref) {
PullCommand pull = git.pull();
try {
PullCommand pull = git.pull();
if (hasText(username)) {
setCredentialsProvider(pull);
}
@@ -229,7 +229,7 @@ public class JGitEnvironmentRepository implements EnvironmentRepository, Initial
}
catch (Exception e) {
logger.warn("Could not pull remote for " + label + " (current ref=" + ref
+ ")");
+ "), remote: " + git.getRepository().getConfig().getString("remote", "origin", "url"));
}
}
@@ -261,7 +261,12 @@ public class JGitEnvironmentRepository implements EnvironmentRepository, Initial
private Git copyFromLocalRepository() throws IOException {
Git git;
FileSystemUtils.copyRecursively(new UrlResource(uri).getFile(), basedir);
File remote = new UrlResource(StringUtils.cleanPath(uri)).getFile();
Assert.state(remote.isDirectory(), "No directory at " + uri);
File gitDir = new File(remote, ".git");
Assert.state(gitDir.exists(), "No .git at " + uri);
Assert.state(gitDir.isDirectory(), "No .git directory at " + uri);
basedir = remote;
git = Git.open(basedir);
return git;
}

View File

@@ -19,7 +19,7 @@ import org.springframework.test.context.web.WebAppConfiguration;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ConfigServerApplication.class)
@IntegrationTest({"server.port:0", "spring.config.name:configserver", "spring.cloud.config.server.git.uri:file:./target/test-classes/config-repo"})
@IntegrationTest({"server.port:0", "spring.config.name:configserver", "spring.cloud.config.server.git.uri:file:./target/repos/config-repo"})
@WebAppConfiguration
@ActiveProfiles("test")
public class ApplicationTests {

View File

@@ -19,8 +19,11 @@ package org.springframework.cloud.config.server;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.util.FileUtils;
import org.junit.After;
import org.junit.Before;
@@ -31,6 +34,8 @@ import org.springframework.cloud.config.Environment;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StreamUtils;
/**
* @author Dave Syer
@@ -47,6 +52,7 @@ public class JGitEnvironmentRepositoryIntegrationTests {
if (basedir.exists()) {
FileUtils.delete(basedir, FileUtils.RECURSIVE);
}
ConfigServerTestUtils.deleteLocalRepo("config-copy");
}
@After
@@ -60,20 +66,42 @@ public class JGitEnvironmentRepositoryIntegrationTests {
public void vanilla() throws IOException {
String uri = ConfigServerTestUtils.prepareLocalRepo();
context = new SpringApplicationBuilder(TestConfiguration.class).web(false)
//TODO: why didn't .properties() work for me?
.run("--spring.cloud.config.server.git.uri=" + uri);
.properties("spring.cloud.config.server.git.uri:" + uri).run();
// TODO: why didn't .properties() work for me?
// .run("--spring.cloud.config.server.git.uri=" + uri);
EnvironmentRepository repository = context.getBean(EnvironmentRepository.class);
repository.findOne("bar", "staging", "master");
Environment environment = repository.findOne("bar", "staging", "master");
assertEquals(2, environment.getPropertySources().size());
}
@Test
public void pull() throws Exception {
ConfigServerTestUtils.prepareLocalRepo();
String uri = ConfigServerTestUtils.copyLocalRepo("config-copy");
context = new SpringApplicationBuilder(TestConfiguration.class).web(false).run(
"--spring.cloud.config.server.git.uri=" + uri);
EnvironmentRepository repository = context.getBean(EnvironmentRepository.class);
repository.findOne("bar", "staging", "master");
Environment environment = repository.findOne("bar", "staging", "master");
assertEquals("bar", environment.getPropertySources().get(0).getSource()
.get("foo"));
Git git = Git.open(ResourceUtils.getFile(uri).getAbsoluteFile());
git.checkout().setName("master").call();
StreamUtils.copy("foo: foo", Charset.defaultCharset(), new FileOutputStream(
ResourceUtils.getFile(uri + "/bar.properties")));
git.add().addFilepattern("bar.properties").call();
git.commit().setMessage("Updated for pull").call();
environment = repository.findOne("bar", "staging", "master");
assertEquals("foo", environment.getPropertySources().get(0).getSource()
.get("foo"));
}
@Test
public void nested() throws IOException {
String uri = ConfigServerTestUtils.prepareLocalRepo("another-config-repo");
context = new SpringApplicationBuilder(TestConfiguration.class)
.web(false)
//TODO: why didn't .properties() work for me?
context = new SpringApplicationBuilder(TestConfiguration.class).web(false)
// TODO: why didn't .properties() work for me?
.run("--spring.cloud.config.server.git.uri=" + uri,
"--spring.cloud.config.server.git.searchPaths=sub");
EnvironmentRepository repository = context.getBean(EnvironmentRepository.class);