Skip to content

Commit b44d8ae

Browse files
committed
Add option to select http client
The plugin depends on network library yavijava https://github.com/jenkinsci/vsphere-cloud-plugin/blob/master/pom.xml#L95-L96 The yavijava supports two types of http clients: `CloseableHttpClient` `HttpURLConnection` - is default The HttpURLConnection client's behaviour can be impacted by system-wide cookie handler CookieManager: https://docs.oracle.com/javase/8/docs/api/java/net/CookieManager.html The major problem is that the CookieManager overlaps logic provided by HttpURLConnection to setup http header `Cookie`. Global CookieManager know nothing about logic of vsphere-cloud-plugin and fill all http headers with stored cookies, as result vsphere center rejects login requests with error: ``` <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soapenv:Body> <soapenv:Fault> <faultcode>ServerFaultCode</faultcode> <faultstring>Cannot complete login due to an incorrect user name or password.</faultstring> <detail> <InvalidLoginFault xmlns="urn:vim25" xsi:type="InvalidLogin"> <faultMessage> <key>vim.fault.InvalidLoginWithReason</key> <arg> <key>1</key> <value xsi:type="xsd:string">vim.fault.InvalidLoginReasonRegisterFailed</value> </arg> <message>Login failed because the session could not be re-registered.</message> </faultMessage> </InvalidLoginFault> </detail> </soapenv:Fault> </soapenv:Body> </soapenv:Envelope> ``` The vsphere-cloud-plugin does not use CookieManager, but there is no guarantee that other plugins avoid it. Example: https://github.com/jenkinsci/parameterized-remote-trigger-plugin/blob/master/src/main/java/org/jenkinsci/plugins/ParameterizedRemoteTrigger/RemoteBuildConfiguration.java#L158-L159 As workaroud we can switch to CloseableHttpClient http client. Issue in Jira: https://issues.jenkins.io/browse/JENKINS-69999
1 parent 0eea7ae commit b44d8ae

File tree

6 files changed

+96
-7
lines changed

6 files changed

+96
-7
lines changed

src/main/java/org/jenkinsci/plugins/vSphereCloud.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ public vSphereCloud(VSphereConnectionConfig vsConnectionConfig, String vsDescrip
172172

173173
public Object readResolve() throws IOException {
174174
if (vsConnectionConfig == null) {
175-
vsConnectionConfig = new VSphereConnectionConfig(vsHost, null);
175+
vsConnectionConfig = new VSphereConnectionConfig(vsHost, null, null);
176176
}
177177
if (this.templates != null) {
178178
for (vSphereCloudSlaveTemplate template : templates) {
@@ -630,9 +630,11 @@ public static List<vSphereCloud> findAllVsphereClouds(String jobName) {
630630
}
631631
}
632632

633-
for (Cloud cloud : Jenkins.getInstance().clouds) {
634-
if (cloud instanceof vSphereCloud) {
635-
vSphereClouds.add((vSphereCloud) cloud);
633+
if (Jenkins.getInstance() != null) {
634+
for (Cloud cloud : Jenkins.getInstance().clouds) {
635+
if (cloud instanceof vSphereCloud) {
636+
vSphereClouds.add((vSphereCloud) cloud);
637+
}
636638
}
637639
}
638640
return vSphereClouds;

src/main/java/org/jenkinsci/plugins/vsphere/VSphereConnectionConfig.java

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
2929
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
3030
import com.cloudbees.plugins.credentials.domains.HostnameRequirement;
31+
import com.vmware.vim25.ws.ApacheHttpClient;
32+
import com.vmware.vim25.ws.WSClient;
3133
import hudson.Extension;
3234
import hudson.Util;
3335
import hudson.model.AbstractDescribableImpl;
@@ -37,11 +39,14 @@
3739
import hudson.util.ListBoxModel;
3840
import hudson.util.Secret;
3941
import java.util.Collections;
42+
import java.util.List;
43+
4044
import edu.umd.cs.findbugs.annotations.CheckForNull;
4145
import edu.umd.cs.findbugs.annotations.NonNull;
4246
import jenkins.model.Jenkins;
4347

4448
import org.apache.commons.lang.StringUtils;
49+
import org.jenkinsci.plugins.vSphereCloud;
4550
import org.jenkinsci.plugins.vsphere.tools.VSphere;
4651
import org.kohsuke.stapler.AncestorInPath;
4752
import org.kohsuke.stapler.DataBoundConstructor;
@@ -58,16 +63,71 @@ public class VSphereConnectionConfig extends AbstractDescribableImpl<VSphereConn
5863
private final @CheckForNull String vsHost;
5964
private /*final*/ boolean allowUntrustedCertificate;
6065
private final @CheckForNull String credentialsId;
61-
66+
67+
private enum HttpClientClassName {
68+
ApacheHttpClientClass("ApacheHttpClient"),
69+
WSClientClass("WSClient");
70+
71+
public final String name;
72+
73+
private HttpClientClassName(String name) {
74+
this.name = name;
75+
}
76+
}
77+
78+
private static Class<?> httpClientNameToClass(String httpClientClassName) {
79+
if (httpClientClassName.equals(HttpClientClassName.ApacheHttpClientClass.name)) {
80+
return ApacheHttpClient.class;
81+
}
82+
return WSClient.class;
83+
}
84+
85+
private static String httpClientClassToName(Class<?> httpClientClass) {
86+
if (httpClientClass == ApacheHttpClient.class) {
87+
return HttpClientClassName.ApacheHttpClientClass.name;
88+
}
89+
return HttpClientClassName.WSClientClass.name;
90+
}
91+
92+
private String httpClientClassName;
93+
94+
private static Class<?> httpClientClass;
95+
96+
public static Class<?> setupGlobalHttpClientClass() {
97+
if (VSphereConnectionConfig.httpClientClass == null) {
98+
VSphereConnectionConfig.httpClientClass = WSClient.class;
99+
List<vSphereCloud> clouds = vSphereCloud.findAllVsphereClouds(null);
100+
if (clouds.size() > 0) {
101+
VSphereConnectionConfig.httpClientClass
102+
= httpClientNameToClass(clouds.get(0).getVsConnectionConfig().httpClientClassName);
103+
}
104+
}
105+
return VSphereConnectionConfig.httpClientClass;
106+
}
107+
108+
public static String getHttpClientClassName() {
109+
return httpClientClassToName(setupGlobalHttpClientClass());
110+
}
111+
112+
@DataBoundSetter
113+
public void setHttpClientClassName(String httpClientClassName) {
114+
this.httpClientClassName = (httpClientClassName == null) ? getHttpClientClassName() : httpClientClassName;
115+
for (vSphereCloud cloud : vSphereCloud.findAllVsphereClouds(null)) {
116+
cloud.getVsConnectionConfig().httpClientClassName = this.httpClientClassName;
117+
}
118+
VSphereConnectionConfig.httpClientClass = httpClientNameToClass(this.httpClientClassName);
119+
}
120+
62121
@DataBoundConstructor
63-
public VSphereConnectionConfig(String vsHost, String credentialsId) {
122+
public VSphereConnectionConfig(String vsHost, String credentialsId, String httpClientClassName) {
64123
this.vsHost = vsHost;
65124
this.credentialsId = credentialsId;
125+
setHttpClientClassName(httpClientClassName);
66126
}
67127

68128
/** Full constructor for internal use, initializes all fields */
69129
public VSphereConnectionConfig(String vsHost, boolean allowUntrustedCertificate, String credentialsId) {
70-
this(vsHost, credentialsId);
130+
this(vsHost, credentialsId, null);
71131
setAllowUntrustedCertificate(allowUntrustedCertificate);
72132
}
73133

@@ -119,6 +179,18 @@ public String getDisplayName() {
119179
return "N/A";
120180
}
121181

182+
public ListBoxModel doFillHttpClientClassNameItems(@AncestorInPath AbstractFolder<?> containingFolderOrNull) {
183+
throwUnlessUserHasPermissionToConfigureCloud(containingFolderOrNull);
184+
185+
ListBoxModel items = new ListBoxModel(new ListBoxModel.Option("Use HttpURLConnection to connect to the vSphere cloud",
186+
VSphereConnectionConfig.HttpClientClassName.WSClientClass.name,
187+
VSphereConnectionConfig.HttpClientClassName.WSClientClass.name.equals(getHttpClientClassName())),
188+
new ListBoxModel.Option("Use CloseableHttpClient to connect to the vSphere cloud",
189+
VSphereConnectionConfig.HttpClientClassName.ApacheHttpClientClass.name,
190+
VSphereConnectionConfig.HttpClientClassName.ApacheHttpClientClass.name.equals(getHttpClientClassName())));
191+
return items;
192+
}
193+
122194
public FormValidation doCheckVsHost(@QueryParameter String value) {
123195
if (value!=null && value.length() != 0) {
124196
if (!value.startsWith("https://")) {

src/main/java/org/jenkinsci/plugins/vsphere/tools/VSphere.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.logging.Logger;
2525
import java.util.logging.Level;
2626

27+
import com.vmware.vim25.ws.ClientCreator;
2728
import edu.umd.cs.findbugs.annotations.CheckForNull;
2829
import edu.umd.cs.findbugs.annotations.NonNull;
2930

@@ -71,6 +72,7 @@ public class VSphere {
7172

7273
private VSphere(@NonNull String url, boolean ignoreCert, @NonNull String user, @CheckForNull String pw) throws VSphereException {
7374
try {
75+
ClientCreator.clientClass = VSphereConnectionConfig.setupGlobalHttpClientClass();
7476
this.url = new URL(url);
7577
final ServiceInstance serviceInstance = new ServiceInstance(this.url, user, pw, ignoreCert);
7678
final ServerConnection serverConnection = serviceInstance.getServerConnection();

src/main/resources/org/jenkinsci/plugins/vsphere/VSphereConnectionConfig/config.groovy

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ f.entry(title:_("vSphere Host"), field:"vsHost") {
77
f.textbox()
88
}
99

10+
f.entry(title:_("Change Http Client"), field:"httpClientClassName") {
11+
f.select()
12+
}
13+
1014
f.entry(title:_("Disable SSL Check"), field:"allowUntrustedCertificate") {
1115
f.checkbox()
1216
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<div>
2+
The <b>vSphere Cloud</b> plugin depends on
3+
<a href="https://github.com/yavijava/yavijava">yavijava</a>
4+
library, which supports two types of http clients:
5+
<a href="https://github.com/yavijava/yavijava/blob/gradle/src/main/java/com/vmware/vim25/ws/ApacheHttpClient.java">CloseableHttpClient</a>
6+
and
7+
<a href="https://github.com/yavijava/yavijava/blob/gradle/src/main/java/com/vmware/vim25/ws/WSClient.java">HttpURLConnection</a>
8+
</div>

src/test/resources/org/jenkinsci/plugins/vsphere/tools/expected_output.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,6 @@
3030
useSnapshot: true
3131
waitForVMTools: true
3232
vsConnectionConfig:
33+
httpClientClassName: "WSClient"
3334
vsHost: "https://company-vsphere"
3435
vsDescription: "Company vSphere"

0 commit comments

Comments
 (0)