version not being updated on first request

New remote branches
New remote tags
— handling a null ref in should pull no longer needed.
— has added benefit of ensuring the local repo is current in case of git failure.
Returns version for tags
New starter code for remote repo testing (where a lot of the complexity lies)
TODO: Still investigating failure of pullDirtyRepo.  I think the test is incorrect but still validating.
This commit is contained in:
rlynch2
2016-11-03 17:38:21 -07:00
parent bc944a67b4
commit 9a3e58e8fe
6 changed files with 294 additions and 113 deletions

View File

@@ -24,22 +24,14 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.*;
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;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.jgit.transport.JschConfigSessionFactory;
import org.eclipse.jgit.transport.OpenSshConfig.Host;
import org.eclipse.jgit.transport.SshSessionFactory;
@@ -60,6 +52,7 @@ import com.jcraft.jsch.Session;
* @author Roy Clarkson
* @author Marcos Barbero
* @author Daniel Lavoie
* @author Ryan Lynch
*/
public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
implements EnvironmentRepository, SearchPathLocator, InitializingBean {
@@ -141,11 +134,7 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
if (label == null) {
label = this.defaultLabel;
}
Ref ref = refresh(application, label);
String version = null;
if (ref != null) {
version = ref.getObjectId().getName();
}
String version = refresh(application, label);
return new Locations(application, profile, label, version,
getSearchLocations(getWorkingDirectory(), application, profile, label));
}
@@ -162,27 +151,29 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
/**
* Get the working directory ready.
*/
private Ref refresh(String application, String label) {
private String refresh(String application, String label) {
initialize();
Git git = null;
try {
git = createGitClient();
git.getRepository().getConfig().setString("branch", label, "merge", label);
Ref ref = checkout(git, label);
if (shouldPull(git, ref)) {
pull(git, label, ref);
//a pull causes the ref retrieved from the checkout to be invalid
//so refreshing it.
ref = git.getRepository().getRef(ref.getName());
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);
if (shouldPull(git)) {
fetch(git, label);
checkout(git, label);
if(isBranch(git, label)) {
merge(git, label);
if (!isClean(git)) {
logger.warn("The local repository is dirty. Resetting it to origin/"
+ label + ".");
resetHard(git, label, "refs/remotes/origin/" + label);
}
}
}
return ref;
else{
checkout(git, label);
}
//always return what is currently HEAD as the version
return git.getRepository().getRef("HEAD").getObjectId().getName();
}
catch (RefNotFoundException e) {
throw new NoSuchLabelException("No such label: " + label);
@@ -237,7 +228,7 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
return checkout.call();
}
/* for testing */ boolean shouldPull(Git git, Ref ref) throws GitAPIException {
/* for testing */ boolean shouldPull(Git git) throws GitAPIException {
boolean shouldPull;
Status gitStatus = git.status().call();
boolean isWorkingTreeClean = gitStatus.isClean();
@@ -249,7 +240,7 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
logDirty(gitStatus);
}
else {
shouldPull = isWorkingTreeClean && ref != null && originUrl != null;
shouldPull = isWorkingTreeClean && originUrl != null;
}
if (!isWorkingTreeClean && !this.forcePull) {
this.logger.info("Cannot pull from remote " + originUrl
@@ -279,33 +270,61 @@ 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);
private FetchResult fetch(Git git, String label) {
FetchCommand fetch = git.fetch().setRemote("origin");
setTimeout(fetch);
try {
if (hasText(getUsername())) {
setCredentialsProvider(fetch);
}
fetch.call();
FetchResult result = fetch.call();
if(result.getTrackingRefUpdates() != null && result.getTrackingRefUpdates().size() > 0) {
this.logger.info("Fetched for remote " + label + " and found " + result.getTrackingRefUpdates().size()
+ " updates");
}
return result;
}
catch (Exception ex) {
this.logger.warn("Could not fetch remote for " + label + " remote: " + git
.getRepository().getConfig().getString("remote", "origin", "url"));
return null;
}
}
private void resetHard(Git git, String label, String ref) {
private MergeResult merge(Git git, String label) {
try {
MergeCommand merge = git.merge();
merge.include(git.getRepository().getRef("origin/" + label));
MergeResult result = merge.call();
if(!result.getMergeStatus().isSuccessful()) {
this.logger.warn("Merged from remote " + label + " with result " + result.getMergeStatus());
}
return result;
}
catch (Exception ex) {
this.logger.warn("Could not merge remote for " + label + " remote: " + git
.getRepository().getConfig().getString("remote", "origin", "url"));
return null;
}
}
private Ref resetHard(Git git, String label, String ref) {
ResetCommand reset = git.reset();
reset.setRef(ref);
reset.setMode(ResetType.HARD);
try {
reset.call();
Ref resetRef = reset.call();
if(resetRef != null) {
this.logger.info("Reset label " + label + " to version " + resetRef.getObjectId());
}
return resetRef;
}
catch (Exception ex) {
this.logger.warn("Could not reset to remote for " + label + " (current ref="
+ ref + "), remote: " + git.getRepository().getConfig()
.getString("remote", "origin", "url"));
return null;
}
}

View File

@@ -19,6 +19,8 @@ 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.cloud.config.environment.Environment;
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.StringUtils;
@@ -111,4 +113,13 @@ public class ConfigServerTestUtils {
return FileSystemUtils.deleteRecursively(dest);
}
public static Object getProperty(Environment env, String sourceNameEndsWith, String property) {
for(PropertySource source: env.getPropertySources()) {
if(source.getName().endsWith(sourceNameEndsWith)) {
return source.getSource().get(property);
}
}
return null;
}
}

View File

@@ -0,0 +1,113 @@
/*
* 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.environment;
import org.eclipse.jgit.api.Git;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.config.server.test.ConfigServerTestUtils;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.ResourceUtils;
import java.io.File;
/**
* Class that holds objects that can be used for testing
*/
public class JGitConfigServerTestData {
public static class LocalGit {
Git git;
File gitWorkingDirectory;
public LocalGit(Git git, File gitWorkingDirectory) {
this.git = git;
this.gitWorkingDirectory = gitWorkingDirectory;
}
public Git getGit() {
return this.git;
}
public File getGitWorkingDirectory() {
return this.gitWorkingDirectory;
}
}
private LocalGit serverGit;
private LocalGit clonedGit;
private JGitEnvironmentRepository repository;
private ConfigurableApplicationContext context;
public JGitConfigServerTestData(LocalGit serverGit, LocalGit clonedGit,
JGitEnvironmentRepository repository, ConfigurableApplicationContext context) {
this.serverGit = serverGit;
this.clonedGit = clonedGit;
this.repository = repository;
this.context = context;
}
public LocalGit getServerGit() {
return this.serverGit;
}
public LocalGit getClonedGit() {
return this.clonedGit;
}
public JGitEnvironmentRepository getRepository() {
return this.repository;
}
public ConfigurableApplicationContext getContext() {
return this.context;
}
public static JGitConfigServerTestData prepareClonedGitRepository(Object... sources) throws Exception {
//setup remote repository
String remoteUri = ConfigServerTestUtils.prepareLocalRepo();
File remoteRepoDir = ResourceUtils.getFile(remoteUri);
Git remoteGit = Git.open(remoteRepoDir.getAbsoluteFile());
remoteGit.checkout().setName("master").call();
//setup local repository
File clonedRepoDir = new File("target/repos/cloned");
if(clonedRepoDir.exists()) {
FileSystemUtils.deleteRecursively(clonedRepoDir);
}else{
clonedRepoDir.mkdirs();
}
Git clonedGit = Git.cloneRepository()
.setURI( "file://" + remoteRepoDir.getAbsolutePath() )
.setDirectory( clonedRepoDir )
.setBranch("master")
.setCloneAllBranches(true)
.call();
//setup our test spring application pointing to the local repo
ConfigurableApplicationContext context = new SpringApplicationBuilder(sources).web(false)
.properties("spring.cloud.config.server.git.uri:" + "file://" + clonedRepoDir.getAbsolutePath()).run();
JGitEnvironmentRepository repository = context.getBean(JGitEnvironmentRepository.class);
return new JGitConfigServerTestData(
new JGitConfigServerTestData.LocalGit(remoteGit, remoteRepoDir),
new JGitConfigServerTestData.LocalGit(clonedGit, clonedRepoDir),
repository, context);
}
}

View File

@@ -44,7 +44,6 @@ 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;
@@ -57,7 +56,6 @@ import org.springframework.cloud.config.server.test.ConfigServerTestUtils;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StreamUtils;
@@ -335,60 +333,30 @@ public class JGitEnvironmentRepositoryIntegrationTests {
@Test
public void testVersionUpdate() throws Exception {
//setup local repository
ConfigServerTestUtils.prepareLocalRepo();
String uri = ConfigServerTestUtils.copyLocalRepo("config-copy");
File localRepoFile = ResourceUtils.getFile(uri);
Git localGit = Git.open(localRepoFile.getAbsoluteFile());
JGitConfigServerTestData testData = JGitConfigServerTestData.prepareClonedGitRepository(TestConfiguration.class);
//setup remote repository
File remoteDir = new File("target/repos/clone");
if(remoteDir.exists()) {
FileSystemUtils.deleteRecursively(remoteDir);
}else{
remoteDir.mkdirs();
}
Git remoteGit = Git.cloneRepository()
.setURI( "file://" + localRepoFile.getAbsolutePath() )
.setDirectory( remoteDir )
.setBranch("master")
.setCloneAllBranches(true)
.call();
StoredConfig config = localGit.getRepository().getConfig();
config.setString("remote", "origin", "url",
remoteDir.getAbsolutePath());
config.setString("remote", "origin", "fetch",
"+refs/heads/*:refs/remotes/origin/*");
config.save();
//get our starting versions
String startingLocalVersion = getCommitID(testData.getClonedGit().getGit(), "master");
String startingRemoteVersion = getCommitID(testData.getServerGit().getGit(), "master");
//get commit ids
String startingLocalVersion = getCommitID(localGit, "master");
String startingRemoteVersion = getCommitID(remoteGit, "master");
//verify the remote and local repo have the same commit ID
assertEquals(startingRemoteVersion, startingLocalVersion);
//setup our test spring application pointing to the local repo
this.context = new SpringApplicationBuilder(TestConfiguration.class).web(false)
.properties("spring.cloud.config.server.git.uri:" + "file://" + localRepoFile.getAbsolutePath()).run();
EnvironmentRepository repository = this.context.getBean(EnvironmentRepository.class);
Environment environment = repository.findOne("bar", "staging", "master");
//make sure we get the right version out of the gate
Environment environment = testData.getRepository().findOne("bar", "staging", "master");
//make sure the environments version is the same as the remote repo version
assertEquals(environment.getVersion(), startingRemoteVersion);
//update the remote repo
FileOutputStream out = new FileOutputStream(remoteDir.getAbsolutePath() + "/bar.properties");
FileOutputStream out = new FileOutputStream(new File(testData.getServerGit().getGitWorkingDirectory(), "bar.properties"));
StreamUtils.copy("foo: foo", Charset.defaultCharset(), out);
remoteGit.add().addFilepattern("bar.properties").call();
remoteGit.commit().setMessage("Updated for pull").call();
testData.getServerGit().getGit().add().addFilepattern("bar.properties").call();
testData.getServerGit().getGit().commit().setMessage("Updated for pull").call();
//pull the environment again which should update the local repo from the just updated remote repo
environment = repository.findOne("bar", "staging", "master");
environment = testData.getRepository().findOne("bar", "staging", "master");
//do some more check outs to get updated version numbers
String updatedLocalVersion = getCommitID(localGit, "master");
String updatedRemoteVersion = getCommitID(remoteGit, "master");
String updatedLocalVersion = getCommitID(testData.getClonedGit().getGit(), "master");
String updatedRemoteVersion = getCommitID(testData.getClonedGit().getGit(), "master");
//make sure our versions have been updated
assertEquals(updatedRemoteVersion, updatedLocalVersion);
@@ -400,6 +368,103 @@ public class JGitEnvironmentRepositoryIntegrationTests {
assertEquals(environment.getVersion(), updatedRemoteVersion);
}
@Test
public void testNewRemoteBranch() throws Exception {
JGitConfigServerTestData testData = JGitConfigServerTestData.prepareClonedGitRepository(TestConfiguration.class);
Environment environment = testData.getRepository().findOne("bar", "staging", "master");
Object fooProperty = ConfigServerTestUtils.getProperty(environment, "bar.properties", "foo");
assertEquals(fooProperty, "bar");
testData.getServerGit().getGit().branchCreate()
.setName("testNewRemoteBranch")
.call();
testData.getServerGit().getGit().checkout()
.setName("testNewRemoteBranch")
.call();
//update the remote repo
FileOutputStream out = new FileOutputStream(
new File(testData.getServerGit().getGitWorkingDirectory(), "/bar.properties"));
StreamUtils.copy("foo: branchBar", Charset.defaultCharset(), out);
testData.getServerGit().getGit().add().addFilepattern("bar.properties").call();
testData.getServerGit().getGit().commit().setMessage("Updated for branch test").call();
environment = testData.getRepository().findOne("bar", "staging", "testNewRemoteBranch");
fooProperty = ConfigServerTestUtils.getProperty(environment, "bar.properties", "foo");
assertEquals(fooProperty, "branchBar");
}
@Test
public void testNewRemoteTag() throws Exception {
JGitConfigServerTestData testData = JGitConfigServerTestData.prepareClonedGitRepository(TestConfiguration.class);
Git serverGit = testData.getServerGit().getGit();
Environment environment = testData.getRepository().findOne("bar", "staging", "master");
Object fooProperty = ConfigServerTestUtils.getProperty(environment, "bar.properties", "foo");
assertEquals(fooProperty, "bar");
serverGit.checkout().setName("master").call();
//create a new tag
serverGit.tag().setName("testTag").setMessage("Testing a tag").call();
//update the remote repo
FileOutputStream out = new FileOutputStream(
new File(testData.getServerGit().getGitWorkingDirectory(), "/bar.properties"));
StreamUtils.copy("foo: testAfterTag", Charset.defaultCharset(), out);
testData.getServerGit().getGit().add().addFilepattern("bar.properties").call();
testData.getServerGit().getGit().commit().setMessage("Updated for branch test").call();
environment = testData.getRepository().findOne("bar", "staging", "master");
fooProperty = ConfigServerTestUtils.getProperty(environment, "bar.properties", "foo");
assertEquals(fooProperty, "testAfterTag");
environment = testData.getRepository().findOne("bar", "staging", "testTag");
fooProperty = ConfigServerTestUtils.getProperty(environment, "bar.properties", "foo");
assertEquals(fooProperty, "bar");
}
@Test
public void testNewCommitID() throws Exception {
JGitConfigServerTestData testData = JGitConfigServerTestData.prepareClonedGitRepository(TestConfiguration.class);
//get our starting versions
String startingRemoteVersion = getCommitID(testData.getServerGit().getGit(), "master");
//make sure we get the right version out of the gate
Environment environment = testData.getRepository().findOne("bar", "staging", "master");
assertEquals(environment.getVersion(), startingRemoteVersion);
//update the remote repo
FileOutputStream out = new FileOutputStream(new File(testData.getServerGit().getGitWorkingDirectory(), "bar.properties"));
StreamUtils.copy("foo: barNewCommit", Charset.defaultCharset(), out);
testData.getServerGit().getGit().add().addFilepattern("bar.properties").call();
testData.getServerGit().getGit().commit().setMessage("Updated for pull").call();
String updatedRemoteVersion = getCommitID(testData.getServerGit().getGit(), "master");
//do a normal request and verify we get the new version
environment = testData.getRepository().findOne("bar", "staging", "master");
assertEquals(environment.getVersion(), updatedRemoteVersion);
Object fooProperty = ConfigServerTestUtils.getProperty(environment, "bar.properties", "foo");
assertEquals(fooProperty, "barNewCommit");
//request the prior commit ID and make sure we get it
environment = testData.getRepository().findOne("bar", "staging", startingRemoteVersion);
assertEquals(environment.getVersion(), startingRemoteVersion);
fooProperty = ConfigServerTestUtils.getProperty(environment, "bar.properties", "foo");
assertEquals(fooProperty, "bar");
}
@Test(expected = NoSuchLabelException.class)
public void testUnknownLabelWithRemote() throws Exception {
JGitConfigServerTestData testData = JGitConfigServerTestData.prepareClonedGitRepository(TestConfiguration.class);
testData.getRepository().findOne("bar", "staging", "BADLabel");
}
private String getCommitID(Git git, String label) throws GitAPIException {
CheckoutCommand checkout = git.checkout();
checkout.setName(label);

View File

@@ -139,8 +139,7 @@ public class JGitEnvironmentRepositoryTests {
assertEquals(2, environment.getPropertySources().size());
assertEquals(this.repository.getUri() + "/bar.properties", environment
.getPropertySources().get(0).getName());
//TODO: why is the version null in tag?
assertNull("version was not null", environment.getVersion());
assertVersion(environment);
}
@Test
@@ -253,41 +252,16 @@ public class JGitEnvironmentRepositoryTests {
this.environment);
repo.setForcePull(true);
boolean shouldPull = repo.shouldPull(git, null);
boolean shouldPull = repo.shouldPull(git);
assertThat("shouldPull was false", shouldPull, is(true));
}
@Test
public void shouldPullForcepullClean() throws Exception {
Git git = mock(Git.class);
StatusCommand statusCommand = mock(StatusCommand.class);
Status status = mock(Status.class);
Repository repository = mock(Repository.class);
StoredConfig storedConfig = mock(StoredConfig.class);
when(git.status()).thenReturn(statusCommand);
when(git.getRepository()).thenReturn(repository);
when(repository.getConfig()).thenReturn(storedConfig);
when(storedConfig.getString("remote", "origin", "url")).thenReturn("http://example/git");
when(statusCommand.call()).thenReturn(status);
when(status.isClean()).thenReturn(true);
JGitEnvironmentRepository repo = new JGitEnvironmentRepository(
this.environment);
repo.setForcePull(true);
boolean shouldPull = repo.shouldPull(git, null);
assertThat("shouldPull was true", shouldPull, is(false));
}
@Test
public void shouldPullNotClean() throws Exception {
Git git = mock(Git.class);
StatusCommand statusCommand = mock(StatusCommand.class);
Status status = mock(Status.class);
Ref ref = mock(Ref.class);
Repository repository = mock(Repository.class);
StoredConfig storedConfig = mock(StoredConfig.class);
@@ -301,7 +275,7 @@ public class JGitEnvironmentRepositoryTests {
JGitEnvironmentRepository repo = new JGitEnvironmentRepository(
this.environment);
boolean shouldPull = repo.shouldPull(git, ref);
boolean shouldPull = repo.shouldPull(git);
assertThat("shouldPull was true", shouldPull, is(false));
}
@@ -311,7 +285,6 @@ public class JGitEnvironmentRepositoryTests {
Git git = mock(Git.class);
StatusCommand statusCommand = mock(StatusCommand.class);
Status status = mock(Status.class);
Ref ref = mock(Ref.class);
Repository repository = mock(Repository.class);
StoredConfig storedConfig = mock(StoredConfig.class);
@@ -325,7 +298,7 @@ public class JGitEnvironmentRepositoryTests {
JGitEnvironmentRepository repo = new JGitEnvironmentRepository(
this.environment);
boolean shouldPull = repo.shouldPull(git, ref);
boolean shouldPull = repo.shouldPull(git);
assertThat("shouldPull was false", shouldPull, is(true));
}

View File

@@ -109,7 +109,7 @@ public class MultipleJGitEnvironmentRepositoryTests {
assertEquals(2, environment.getPropertySources().size());
assertEquals(this.repository.getUri() + "/bar.properties", environment
.getPropertySources().get(0).getName());
assertNull("version was not null", environment.getVersion());
assertVersion(environment);
}
@Test