Propagate failure of ssh command to caller of SshSupport

When SshSupport.runSshCommand fails since the executed external ssh
command failed throw a CommandFailedException.

If discovery of LFS server fails due to failure of the
git-lfs-authenticate command chain the CommandFailureException to the
LfsConfigInvalidException in order to allow root cause analysis in the
application using that.

Change-Id: I2f9ea2be11274549f6d845937164c248b3d840b2
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
This commit is contained in:
Matthias Sohn 2018-06-12 14:09:39 +02:00
parent 5429d1a0cf
commit f7fbc7fcd7
7 changed files with 62 additions and 28 deletions

View File

@ -1,4 +1,3 @@
cannotDiscoverLfs=Cannot discover LFS URI
corruptLongObject=The content hash ''{0}'' of the long object ''{1}'' doesn''t match its id, the corrupt object will be deleted. corruptLongObject=The content hash ''{0}'' of the long object ''{1}'' doesn''t match its id, the corrupt object will be deleted.
incorrectLONG_OBJECT_ID_LENGTH=Incorrect LONG_OBJECT_ID_LENGTH. incorrectLONG_OBJECT_ID_LENGTH=Incorrect LONG_OBJECT_ID_LENGTH.
inconsistentMediafileLength=Mediafile {0} has unexpected length; expected {1} but found {2}. inconsistentMediafileLength=Mediafile {0} has unexpected length; expected {1} but found {2}.

View File

@ -63,4 +63,17 @@ public LfsConfigInvalidException(String msg) {
super(msg); super(msg);
} }
/**
* Constructor for LfsConfigInvalidException.
*
* @param msg
* the error description
* @param e
* cause of this exception
* @since 5.0
*/
public LfsConfigInvalidException(String msg, Exception e) {
super(msg, e);
}
} }

View File

@ -49,6 +49,7 @@
import java.io.IOException; import java.io.IOException;
import java.net.ProxySelector; import java.net.ProxySelector;
import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.LinkedList; import java.util.LinkedList;
@ -56,6 +57,7 @@
import java.util.TreeMap; import java.util.TreeMap;
import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.errors.CommandFailedException;
import org.eclipse.jgit.lfs.LfsPointer; import org.eclipse.jgit.lfs.LfsPointer;
import org.eclipse.jgit.lfs.Protocol; import org.eclipse.jgit.lfs.Protocol;
import org.eclipse.jgit.lfs.errors.LfsConfigInvalidException; import org.eclipse.jgit.lfs.errors.LfsConfigInvalidException;
@ -68,17 +70,12 @@
import org.eclipse.jgit.transport.http.HttpConnection; import org.eclipse.jgit.transport.http.HttpConnection;
import org.eclipse.jgit.util.HttpSupport; import org.eclipse.jgit.util.HttpSupport;
import org.eclipse.jgit.util.SshSupport; import org.eclipse.jgit.util.SshSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* Provides means to get a valid LFS connection for a given repository. * Provides means to get a valid LFS connection for a given repository.
*/ */
public class LfsConnectionFactory { public class LfsConnectionFactory {
private static final Logger log = LoggerFactory
.getLogger(LfsConnectionFactory.class);
private static final int SSH_AUTH_TIMEOUT_SECONDS = 30; private static final int SSH_AUTH_TIMEOUT_SECONDS = 30;
private static final String SCHEME_HTTPS = "https"; //$NON-NLS-1$ private static final String SCHEME_HTTPS = "https"; //$NON-NLS-1$
private static final String SCHEME_SSH = "ssh"; //$NON-NLS-1$ private static final String SCHEME_SSH = "ssh"; //$NON-NLS-1$
@ -133,6 +130,7 @@ private static String getLfsUrl(Repository db, String purpose,
String lfsUrl = config.getString(ConfigConstants.CONFIG_SECTION_LFS, String lfsUrl = config.getString(ConfigConstants.CONFIG_SECTION_LFS,
null, null,
ConfigConstants.CONFIG_KEY_URL); ConfigConstants.CONFIG_KEY_URL);
Exception ex = null;
if (lfsUrl == null) { if (lfsUrl == null) {
String remoteUrl = null; String remoteUrl = null;
for (String remote : db.getRemoteNames()) { for (String remote : db.getRemoteNames()) {
@ -151,39 +149,44 @@ private static String getLfsUrl(Repository db, String purpose,
break; break;
} }
if (lfsUrl == null && remoteUrl != null) { if (lfsUrl == null && remoteUrl != null) {
lfsUrl = discoverLfsUrl(db, purpose, additionalHeaders, try {
remoteUrl); lfsUrl = discoverLfsUrl(db, purpose, additionalHeaders,
remoteUrl);
} catch (URISyntaxException | IOException
| CommandFailedException e) {
ex = e;
}
} else { } else {
lfsUrl = lfsUrl + Protocol.INFO_LFS_ENDPOINT; lfsUrl = lfsUrl + Protocol.INFO_LFS_ENDPOINT;
} }
} }
if (lfsUrl == null) { if (lfsUrl == null) {
if (ex != null) {
throw new LfsConfigInvalidException(
LfsText.get().lfsNoDownloadUrl, ex);
}
throw new LfsConfigInvalidException(LfsText.get().lfsNoDownloadUrl); throw new LfsConfigInvalidException(LfsText.get().lfsNoDownloadUrl);
} }
return lfsUrl; return lfsUrl;
} }
private static String discoverLfsUrl(Repository db, String purpose, private static String discoverLfsUrl(Repository db, String purpose,
Map<String, String> additionalHeaders, String remoteUrl) { Map<String, String> additionalHeaders, String remoteUrl)
try { throws URISyntaxException, IOException, CommandFailedException {
URIish u = new URIish(remoteUrl); URIish u = new URIish(remoteUrl);
if (u.getScheme() == null || SCHEME_SSH.equals(u.getScheme())) { if (u.getScheme() == null || SCHEME_SSH.equals(u.getScheme())) {
Protocol.ExpiringAction action = getSshAuthentication( Protocol.ExpiringAction action = getSshAuthentication(db, purpose,
db, purpose, remoteUrl, u); remoteUrl, u);
additionalHeaders.putAll(action.header); additionalHeaders.putAll(action.header);
return action.href; return action.href;
} else { } else {
return remoteUrl + Protocol.INFO_LFS_ENDPOINT; return remoteUrl + Protocol.INFO_LFS_ENDPOINT;
}
} catch (Exception e) {
log.error(LfsText.get().cannotDiscoverLfs, e);
return null; // could not discover
} }
} }
private static Protocol.ExpiringAction getSshAuthentication( private static Protocol.ExpiringAction getSshAuthentication(
Repository db, String purpose, String remoteUrl, URIish u) Repository db, String purpose, String remoteUrl, URIish u)
throws IOException { throws IOException, CommandFailedException {
AuthCache cached = sshAuthCache.get(remoteUrl); AuthCache cached = sshAuthCache.get(remoteUrl);
Protocol.ExpiringAction action = null; Protocol.ExpiringAction action = null;
if (cached != null && cached.validUntil > System.currentTimeMillis()) { if (cached != null && cached.validUntil > System.currentTimeMillis()) {

View File

@ -60,7 +60,6 @@ public static LfsText get() {
} }
// @formatter:off // @formatter:off
/***/ public String cannotDiscoverLfs;
/***/ public String corruptLongObject; /***/ public String corruptLongObject;
/***/ public String inconsistentMediafileLength; /***/ public String inconsistentMediafileLength;
/***/ public String inconsistentContentLength; /***/ public String inconsistentContentLength;

View File

@ -629,6 +629,7 @@ sourceIsNotAWildcard=Source is not a wildcard.
sourceRefDoesntResolveToAnyObject=Source ref {0} doesn''t resolve to any object. sourceRefDoesntResolveToAnyObject=Source ref {0} doesn''t resolve to any object.
sourceRefNotSpecifiedForRefspec=Source ref not specified for refspec: {0} sourceRefNotSpecifiedForRefspec=Source ref not specified for refspec: {0}
squashCommitNotUpdatingHEAD=Squash commit -- not updating HEAD squashCommitNotUpdatingHEAD=Squash commit -- not updating HEAD
sshCommandFailed=Execution of ssh command ''{0}'' failed with error ''{1}''
sshUserNameError=Jsch error: failed to set SSH user name correctly to ''{0}''; using ''{1}'' picked up from SSH config file. sshUserNameError=Jsch error: failed to set SSH user name correctly to ''{0}''; using ''{1}'' picked up from SSH config file.
sslFailureExceptionMessage=Secure connection to {0} could not be stablished because of SSL problems sslFailureExceptionMessage=Secure connection to {0} could not be stablished because of SSL problems
sslFailureInfo=A secure connection to {0}\ncould not be established because the server''s certificate could not be validated. sslFailureInfo=A secure connection to {0}\ncould not be established because the server''s certificate could not be validated.

View File

@ -690,6 +690,7 @@ public static JGitText get() {
/***/ public String sourceRefDoesntResolveToAnyObject; /***/ public String sourceRefDoesntResolveToAnyObject;
/***/ public String sourceRefNotSpecifiedForRefspec; /***/ public String sourceRefNotSpecifiedForRefspec;
/***/ public String squashCommitNotUpdatingHEAD; /***/ public String squashCommitNotUpdatingHEAD;
/***/ public String sshCommandFailed;
/***/ public String sshUserNameError; /***/ public String sshUserNameError;
/***/ public String sslFailureExceptionMessage; /***/ public String sslFailureExceptionMessage;
/***/ public String sslFailureInfo; /***/ public String sslFailureInfo;

View File

@ -43,8 +43,11 @@
package org.eclipse.jgit.util; package org.eclipse.jgit.util;
import java.io.IOException; import java.io.IOException;
import java.text.MessageFormat;
import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.CommandFailedException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.RemoteSession; import org.eclipse.jgit.transport.RemoteSession;
import org.eclipse.jgit.transport.SshSessionFactory; import org.eclipse.jgit.transport.SshSessionFactory;
@ -75,18 +78,23 @@ public class SshSupport {
* @param timeout * @param timeout
* a timeout in seconds. The timeout may be exceeded in corner * a timeout in seconds. The timeout may be exceeded in corner
* cases. * cases.
* @return The entire output read from stdout. Stderr is discarded. * @return The entire output read from stdout.
* @throws IOException * @throws IOException
* @throws CommandFailedException
* if the ssh command execution failed, error message contains
* the content of stderr.
*/ */
public static String runSshCommand(URIish sshUri, public static String runSshCommand(URIish sshUri,
@Nullable CredentialsProvider provider, FS fs, String command, @Nullable CredentialsProvider provider, FS fs, String command,
int timeout) throws IOException { int timeout) throws IOException, CommandFailedException {
RemoteSession session = null; RemoteSession session = null;
Process process = null; Process process = null;
StreamCopyThread errorThread = null; StreamCopyThread errorThread = null;
StreamCopyThread outThread = null; StreamCopyThread outThread = null;
try (MessageWriter stderr = new MessageWriter(); CommandFailedException failure = null;
MessageWriter stdout = new MessageWriter()) { @SuppressWarnings("resource")
MessageWriter stderr = new MessageWriter();
try (MessageWriter stdout = new MessageWriter()) {
session = SshSessionFactory.getInstance().getSession(sshUri, session = SshSessionFactory.getInstance().getSession(sshUri,
provider, fs, 1000 * timeout); provider, fs, 1000 * timeout);
process = session.exec(command, 0); process = session.exec(command, 0);
@ -127,11 +135,21 @@ public static String runSshCommand(URIish sshUri,
} }
} }
if (process != null) { if (process != null) {
if (process.exitValue() != 0) {
failure = new CommandFailedException(process.exitValue(),
MessageFormat.format(
JGitText.get().sshCommandFailed, command,
stderr.toString()));
}
process.destroy(); process.destroy();
} }
stderr.close();
if (session != null) { if (session != null) {
SshSessionFactory.getInstance().releaseSession(session); SshSessionFactory.getInstance().releaseSession(session);
} }
if (failure != null) {
throw failure;
}
} }
} }