This commit is contained in:
Ryan Baxter
2016-09-06 13:50:28 -04:00
3 changed files with 146 additions and 1 deletions

View File

@@ -24,14 +24,18 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
import org.eclipse.jgit.api.FetchCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ListBranchCommand;
import org.eclipse.jgit.api.ListBranchCommand.ListMode;
import org.eclipse.jgit.api.PullCommand;
import org.eclipse.jgit.api.ResetCommand;
import org.eclipse.jgit.api.ResetCommand.ResetType;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.api.StatusCommand;
import org.eclipse.jgit.api.TransportCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
@@ -55,6 +59,7 @@ import com.jcraft.jsch.Session;
* @author Dave Syer
* @author Roy Clarkson
* @author Marcos Barbero
* @author Daniel Lavoie
*/
public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
implements EnvironmentRepository, SearchPathLocator, InitializingBean {
@@ -166,6 +171,14 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
Ref ref = checkout(git, label);
if (shouldPull(git, ref)) {
pull(git, label, ref);
if (!isClean(git)) {
logger.warn("The local repository is dirty. Reseting it to origin/"
+ label + ".");
fetch(git, label, "origin");
resetHard(git, label, "refs/remotes/origin/" + label);
}
}
return ref;
}
@@ -264,6 +277,36 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
return isBranch(git, label) && !isLocalBranch(git, label);
}
private void fetch(Git git, String label, String remote) {
FetchCommand fetch = git.fetch().setRemote(remote);
setTimeout(fetch);
try {
if (hasText(getUsername())) {
setCredentialsProvider(fetch);
}
fetch.call();
}
catch (Exception ex) {
this.logger.warn("Could not fetch remote for " + label + " remote: " + git
.getRepository().getConfig().getString("remote", "origin", "url"));
}
}
private void resetHard(Git git, String label, String ref) {
ResetCommand reset = git.reset();
reset.setRef(ref);
reset.setMode(ResetType.HARD);
try {
reset.call();
}
catch (Exception ex) {
this.logger.warn("Could not reset to remote for " + label + " (current ref="
+ ref + "), remote: " + git.getRepository().getConfig()
.getString("remote", "origin", "url"));
}
}
/**
* Assumes we are on a tracking branch (should be safe)
*/
@@ -369,6 +412,20 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
pull.setTimeout(this.timeout);
}
private boolean isClean(Git git) {
StatusCommand status = git.status();
try {
return status.call().isClean();
}
catch (Exception e) {
this.logger
.warn("Could not execute status command on local repository. Cause: ("
+ e.getClass().getSimpleName() + ") " + e.getMessage());
return false;
}
}
private void trackBranch(Git git, CheckoutCommand checkout, String label) {
checkout.setCreateBranch(true).setName(label)
.setUpstreamMode(SetupUpstreamMode.TRACK)

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.cloud.config.server.test;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.StringUtils;
@@ -24,8 +27,22 @@ import java.io.IOException;
/**
* @author Dave Syer
* @author Daniel Lavoie
*/
public class ConfigServerTestUtils {
public static Repository prepareBareRemote() throws IOException {
// Create a folder in the temp folder that will act as the remote repository
File remoteDir = File.createTempFile("remote", "");
remoteDir.delete();
remoteDir.mkdirs();
// Create a bare repository
FileKey fileKey = FileKey.exact(remoteDir, FS.DETECTED);
Repository remoteRepo = fileKey.open(false);
remoteRepo.create(true);
return remoteRepo;
}
public static String prepareLocalRepo() throws IOException {
return prepareLocalRepo("./", "target/repos", "config-repo", "target/config");

View File

@@ -26,12 +26,21 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ResetCommand.ResetType;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.util.FileUtils;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
@@ -48,6 +57,7 @@ import org.springframework.util.StreamUtils;
/**
* @author Dave Syer
* @author Roy Clarkson
* @author Daniel Lavoie
*/
public class JGitEnvironmentRepositoryIntegrationTests {
@@ -108,6 +118,67 @@ public class JGitEnvironmentRepositoryIntegrationTests {
environment.getPropertySources().get(0).getSource().get("foo"));
}
/**
* Tests a special use case where the remote repository has been updated with a forced
* push conflicting with the local repo of the Config Server. The Config Server has to
* reset hard on the new reference because a simple pull operation could result in a
* conflicting local repository.
*/
@Test
public void pullDirtyRepo() throws Exception {
ConfigServerTestUtils.prepareLocalRepo();
String uri = ConfigServerTestUtils.copyLocalRepo("config-copy");
// Create a remote bare repository.
Repository remote = ConfigServerTestUtils.prepareBareRemote();
Git git = Git.open(ResourceUtils.getFile(uri).getAbsoluteFile());
StoredConfig config = git.getRepository().getConfig();
config.setString("remote", "origin", "url",
remote.getDirectory().getAbsolutePath());
config.setString("remote", "origin", "fetch",
"+refs/heads/*:refs/remotes/origin/*");
config.save();
// Pushes the raw branch to remote repository.
git.push().call();
String commitToRevertBeforePull = git.log().setMaxCount(1).call().iterator()
.next().getName();
this.context = new SpringApplicationBuilder(TestConfiguration.class).web(false)
.run("--spring.cloud.config.server.git.uri=" + uri);
JGitEnvironmentRepository repository = this.context
.getBean(JGitEnvironmentRepository.class);
// Fetches the repository for the first time.
repository.getLocations("bar", "test", "raw");
// Resets to the original commit.
git.reset().setMode(ResetType.HARD).setRef("master").call();
// Generate a conflicting commit who will be forced on the origin.
Path applicationFilePath = Paths
.get(ResourceUtils.getFile(uri).getAbsoluteFile() + "/application.yml");
Files.write(applicationFilePath,
Arrays.asList("info:", " foo: bar", "raw: false"),
StandardCharsets.UTF_8, StandardOpenOption.TRUNCATE_EXISTING);
git.add().addFilepattern(".").call();
git.commit().setMessage("Conflicting commit.").call();
git.push().setForce(true).call();
// Reset to the raw branch.
git.reset().setMode(ResetType.HARD).setRef(commitToRevertBeforePull).call();
// Triggers the repository refresh.
repository.getLocations("bar", "test", "raw");
Assert.assertTrue("Local repository is not cleaned after retreiving resources.",
git.status().call().isClean());
}
@Test
public void nested() throws IOException {
String uri = ConfigServerTestUtils.prepareLocalRepo("another-config-repo");