Skip to content
Permalink
Browse files

Merge pull request #72 from jenkinsci/handle_proxy_settings

[JENKINS-6933] Handle proxy settings
  • Loading branch information...
olivierdagenais committed Feb 26, 2016
2 parents dfe4480 + 28552ce commit 2fb117505b6d4b45e5c883e8045d182c886a86de
Showing with 657 additions and 21 deletions.
  1. +6 −0 pom.xml
  2. +3 −0 src/main/java/hudson/plugins/tfs/TeamFoundationServerScm.java
  3. +4 −1 src/main/java/hudson/plugins/tfs/commands/AbstractCallableCommand.java
  4. +3 −0 src/main/java/hudson/plugins/tfs/commands/ServerConfigurationProvider.java
  5. +6 −0 src/main/java/hudson/plugins/tfs/model/MockableVersionControlClient.java
  6. +24 −0 src/main/java/hudson/plugins/tfs/model/ModernConnectionAdvisor.java
  7. +27 −0 src/main/java/hudson/plugins/tfs/model/ModernHTTPClientFactory.java
  8. +52 −1 src/main/java/hudson/plugins/tfs/model/Server.java
  9. +134 −0 src/main/java/hudson/plugins/tfs/model/WebProxySettings.java
  10. +128 −10 src/test/java/hudson/plugins/tfs/FunctionalTest.java
  11. +26 −9 src/test/java/hudson/plugins/tfs/IntegrationTestHelper.java
  12. +69 −0 src/test/java/hudson/plugins/tfs/InterceptingTaskListener.java
  13. +62 −0 src/test/java/hudson/plugins/tfs/LoggingFiltersSourceAdapter.java
  14. +26 −0 src/test/java/hudson/plugins/tfs/commands/AbstractCallableCommandTest.java
  15. +4 −0 src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/config.xml
  16. +4 −0 ...udson/plugins/tfs/FunctionalTest/useWebProxyServer/hudson.plugins.tfs.TeamFoundationServerScm.xml
  17. BIN src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/identity.key.enc
  18. +30 −0 .../tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/2015-07-15_20-37-42/build.xml
  19. +3 −0 .../FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/2015-07-15_20-37-42/changelog.xml
  20. +3 −0 ...lugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/2015-07-15_20-37-42/log
  21. +1 −0 ...hudson/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/lastFailedBuild
  22. +1 −0 ...hudson/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/lastStableBuild
  23. +1 −0 ...on/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/lastSuccessfulBuild
  24. +1 −0 ...dson/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/lastUnstableBuild
  25. +1 −0 .../plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/lastUnsuccessfulBuild
  26. 0 ...urces/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/builds/legacyIds
  27. +30 −0 ...t/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/config.xml
  28. +1 −0 ...ources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/jobs/useWebProxyServer/nextBuildNumber
  29. +6 −0 src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/proxy.xml
  30. +1 −0 src/test/resources/hudson/plugins/tfs/FunctionalTest/useWebProxyServer/secret.key
@@ -241,6 +241,12 @@
<version>1.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.littleshoot</groupId>
<artifactId>littleproxy</artifactId>
<version>1.1.0-beta1</version>
<scope>test</scope>
</dependency>
</dependencies>

<repositories>
@@ -565,6 +565,9 @@ protected PollingResult compareRemoteRevisionWith(
? Change.NONE
: Change.SIGNIFICANT;
return new PollingResult(tfsBaseline, tfsRemote, change);
} catch (final Exception e) {
e.printStackTrace(listener.fatalError(e.getMessage()));
return PollingResult.NO_CHANGES;
} finally {
server.close();
}
@@ -2,6 +2,7 @@

import hudson.model.TaskListener;
import hudson.plugins.tfs.model.Server;
import hudson.plugins.tfs.model.WebProxySettings;
import hudson.remoting.Callable;

import java.io.IOException;
@@ -13,16 +14,18 @@
private final String userName;
private final String userPassword;
private final TaskListener listener;
private final WebProxySettings webProxySettings;

protected AbstractCallableCommand(final ServerConfigurationProvider serverConfig) {
url = serverConfig.getUrl();
userName = serverConfig.getUserName();
userPassword = serverConfig.getUserPassword();
listener = serverConfig.getListener();
webProxySettings = serverConfig.getWebProxySettings();
}

public Server createServer() throws IOException {
final Server server = new Server(null, listener, url, userName, userPassword);
final Server server = new Server(null, listener, url, userName, userPassword, webProxySettings);
return server;
}

@@ -1,6 +1,7 @@
package hudson.plugins.tfs.commands;

import hudson.model.TaskListener;
import hudson.plugins.tfs.model.WebProxySettings;

public interface ServerConfigurationProvider {

@@ -11,4 +12,6 @@
public String getUserPassword();

public TaskListener getListener();

public WebProxySettings getWebProxySettings();
}
@@ -1,5 +1,6 @@
package hudson.plugins.tfs.model;

import com.microsoft.tfs.core.TFSTeamProjectCollection;
import com.microsoft.tfs.core.clients.versioncontrol.VersionControlClient;
import com.microsoft.tfs.core.clients.versioncontrol.WorkspaceLocation;
import com.microsoft.tfs.core.clients.versioncontrol.WorkspaceOptions;
@@ -73,6 +74,11 @@ public void deleteWorkspace(final Workspace workspace) {
vcc.deleteWorkspace(workspace);
}

public TFSTeamProjectCollection getConnection() {
makeSureNotClosed();
return vcc.getConnection();
}

public VersionControlEventEngine getEventEngine() {
makeSureNotClosed();
return vcc.getEventEngine();
@@ -0,0 +1,24 @@
package hudson.plugins.tfs.model;

import java.util.Locale;
import java.util.TimeZone;

import com.microsoft.tfs.core.config.ConnectionInstanceData;
import com.microsoft.tfs.core.config.DefaultConnectionAdvisor;
import com.microsoft.tfs.core.config.httpclient.HTTPClientFactory;
import com.microsoft.tfs.core.httpclient.ProxyHost;

public class ModernConnectionAdvisor extends DefaultConnectionAdvisor {

private final ProxyHost proxyHost;

public ModernConnectionAdvisor(final ProxyHost proxyHost) {
super(Locale.getDefault(), TimeZone.getDefault());
this.proxyHost = proxyHost;
}

@Override
public HTTPClientFactory getHTTPClientFactory(final ConnectionInstanceData connectionInstanceData) {
return new ModernHTTPClientFactory(connectionInstanceData, proxyHost);
}
}
@@ -0,0 +1,27 @@
package hudson.plugins.tfs.model;

import com.microsoft.tfs.core.config.ConnectionInstanceData;
import com.microsoft.tfs.core.config.httpclient.DefaultHTTPClientFactory;
import com.microsoft.tfs.core.httpclient.HostConfiguration;
import com.microsoft.tfs.core.httpclient.HttpClient;
import com.microsoft.tfs.core.httpclient.HttpState;
import com.microsoft.tfs.core.httpclient.ProxyHost;

public class ModernHTTPClientFactory extends DefaultHTTPClientFactory {

private final ProxyHost proxyHost;

public ModernHTTPClientFactory(final ConnectionInstanceData connectionInstanceData) {
this(connectionInstanceData, null);
}

public ModernHTTPClientFactory(final ConnectionInstanceData connectionInstanceData, final ProxyHost proxyHost) {
super(connectionInstanceData);
this.proxyHost = proxyHost;
}

@Override
public void configureClientProxy(final HttpClient httpClient, final HostConfiguration hostConfiguration,final HttpState httpState, final ConnectionInstanceData connectionInstanceData) {
hostConfiguration.setProxyHost(proxyHost);
}
}
@@ -5,7 +5,9 @@
import com.microsoft.tfs.core.clients.webservices.IIdentityManagementService;
import com.microsoft.tfs.core.clients.webservices.IdentityManagementException;
import com.microsoft.tfs.core.clients.webservices.IdentityManagementService;
import com.microsoft.tfs.core.httpclient.ProxyHost;
import hudson.Launcher;
import hudson.ProxyConfiguration;
import hudson.model.TaskListener;
import hudson.plugins.tfs.commands.ServerConfigurationProvider;

@@ -28,6 +30,7 @@
import com.microsoft.tfs.util.Closable;
import hudson.remoting.Callable;
import hudson.remoting.VirtualChannel;
import jenkins.model.Jenkins;

public class Server implements ServerConfigurationProvider, Closable {

@@ -40,9 +43,14 @@
private final Launcher launcher;
private final TaskListener taskListener;
private final TFSTeamProjectCollection tpc;
private final WebProxySettings webProxySettings;
private MockableVersionControlClient mockableVcc;

public Server(final Launcher launcher, final TaskListener taskListener, final String url, final String username, final String password) throws IOException {
this(launcher, taskListener, url, username, password, null);
}

public Server(final Launcher launcher, final TaskListener taskListener, final String url, final String username, final String password, final WebProxySettings webProxySettings) throws IOException {
this.launcher = launcher;
this.taskListener = taskListener;
this.url = url;
@@ -63,9 +71,21 @@ else if (username != null && password != null) {
}

if (credentials != null) {
this.tpc = new TFSTeamProjectCollection(uri, credentials);
if (webProxySettings != null) {
this.webProxySettings = webProxySettings;
}
else {
final VirtualChannel channel = launcher != null ? launcher.getChannel() : null;
final ProxyConfiguration proxyConfiguration = determineProxyConfiguration(channel);
this.webProxySettings = new WebProxySettings(proxyConfiguration);
}
final String host = uri.getHost();
final ProxyHost proxyHost = this.webProxySettings.toProxyHost(host);
final ModernConnectionAdvisor advisor = new ModernConnectionAdvisor(proxyHost);
this.tpc = new TFSTeamProjectCollection(uri, credentials, advisor);
}
else {
this.webProxySettings = null;
this.tpc = null;
}
}
@@ -74,6 +94,33 @@ else if (username != null && password != null) {
this(null, null, url, null, null);
}

static ProxyConfiguration determineProxyConfiguration(final VirtualChannel channel) {
final Jenkins jenkins = Jenkins.getInstance();
final ProxyConfiguration proxyConfiguration;
if (jenkins == null) {
if (channel != null) {
try {
proxyConfiguration = channel.call(new Callable<ProxyConfiguration, Throwable>() {
public ProxyConfiguration call() throws Throwable {
final Jenkins jenkins = Jenkins.getInstance();
final ProxyConfiguration result = jenkins != null ? jenkins.proxy : null;
return result;
}
});
} catch (final Throwable throwable) {
throw new Error(throwable);
}
}
else {
proxyConfiguration = null;
}
}
else {
proxyConfiguration = jenkins.proxy;
}
return proxyConfiguration;
}

public Project getProject(String projectPath) {
if (! projects.containsKey(projectPath)) {
projects.put(projectPath, new Project(this, projectPath));
@@ -127,6 +174,10 @@ public Launcher getLauncher() {
return launcher;
}

public WebProxySettings getWebProxySettings() {
return webProxySettings;
}

public TaskListener getListener() {
return taskListener;
}
@@ -0,0 +1,134 @@
package hudson.plugins.tfs.model;

import com.microsoft.tfs.core.httpclient.ProxyHost;
import hudson.ProxyConfiguration;
import hudson.util.Secret;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;

/**
* A {@link Serializable} adapter between {@link ProxyConfiguration} and {@link ProxyHost}.
*/
public class WebProxySettings implements Serializable {
private static final long serialVersionUID = 401L;

private final String hostName;
private final int port;
private final String proxyUser;
private final Secret proxySecret;
private final List<Pattern> noProxyHostPatterns;

@SuppressWarnings("unused" /* Needed by Serializable interface */)
private WebProxySettings() {
this(null, -1, null, null, null);
}

/**
* Convenience constructor, mostly for tests.
*
* @param hostName the name (or address) of the proxy server.
* May be {@code null}, meaning there is no proxy server configured.
* @param port the port that the proxy server is listening on
* @param noProxyHostPatterns a list of {@link Pattern} representing hosts that should not be proxied.
* May be {@code null}, meaning all hosts will be proxied.
* @param proxyUser the name of the user with which to authenticate to the proxy server.
* May be {@code null}, meaning the proxy server doesn't need authentication.
* @param proxySecret the password of the user with which to authenticate to the proxy server.
* May be {@code null}, meaning the proxy server doesn't need authentication.
*/
public WebProxySettings(final String hostName, final int port, final List<Pattern> noProxyHostPatterns, final String proxyUser, final Secret proxySecret) {
this.hostName = hostName;
this.port = port;
this.noProxyHostPatterns = copyNoProxyHostPatterns(noProxyHostPatterns);
this.proxyUser = proxyUser;
this.proxySecret = proxySecret;
}

/**
* Initialize a {@link WebProxySettings} from a Jenkins {@link ProxyConfiguration}.
*
* @param proxyConfiguration the proxy settings as obtained from Jenkins.
* May be {@code null}, meaning there is no proxy configured.
*/
public WebProxySettings(final ProxyConfiguration proxyConfiguration) {
if (proxyConfiguration != null) {
this.hostName = proxyConfiguration.name;
this.port = proxyConfiguration.port;
this.proxyUser = proxyConfiguration.getUserName();
this.noProxyHostPatterns = copyNoProxyHostPatterns(proxyConfiguration.getNoProxyHostPatterns());
this.proxySecret = Secret.fromString(proxyConfiguration.getEncryptedPassword());
}
else {
this.hostName = null;
this.port = -1;
this.proxyUser = null;
this.proxySecret = null;
this.noProxyHostPatterns = copyNoProxyHostPatterns(null);
}
}

private static ArrayList<Pattern> copyNoProxyHostPatterns(final List<Pattern> noProxyHostPatterns) {
return new ArrayList<Pattern>(
noProxyHostPatterns == null
? Collections.<Pattern>emptyList()
: noProxyHostPatterns
);
}

/**
* Initialize a {@link ProxyHost} from this {@link WebProxySettings} for the provided hostToProxy.
* May return null, which either means there is no proxy server configured or it does not apply
* to the provided hostToProxy.
*
* @param hostToProxy the name of the host for which proxying is considered.
* @return an instance of {@link ProxyHost} or {@code null} if no proxy is to be used.
*/
public ProxyHost toProxyHost(final String hostToProxy) {
final ProxyHost proxyHost;
if (this.hostName != null) {
final boolean shouldProxy = shouldProxy(hostToProxy, noProxyHostPatterns);
if (shouldProxy) {
// TODO: The version of httpclient used by the TFS SDK does not support proxy auth
proxyHost = new ProxyHost(hostName, port);
} else {
proxyHost = null;
}
}
else {
proxyHost = null;
}
return proxyHost;
}

static boolean shouldProxy(final String host, final List<Pattern> noProxyHostPatterns) {
// inspired by https://github.com/jenkinsci/git-client-plugin/commit/2fefeae06db79d09d6604994001f8f2bd21549e1
boolean shouldProxy = true;
for (final Pattern p : noProxyHostPatterns) {
if (p.matcher(host).matches()) {
shouldProxy = false;
break;
}
}
return shouldProxy;
}

public String getHostName() {
return hostName;
}

public int getPort() {
return port;
}

public String getProxyUser() {
return proxyUser;
}

public Secret getProxySecret() {
return proxySecret;
}
}

0 comments on commit 2fb1175

Please sign in to comment.
You can’t perform that action at this time.