Skip to content

Commit aca23f6

Browse files
committed
Merge pull request #16 from oleg-nenashev/JENKINS-25588
[JENKINS-25588] - Migrate vSphereCloud to Credentials plugin
2 parents 82af1af + 5d7242a commit aca23f6

File tree

6 files changed

+232
-33
lines changed

6 files changed

+232
-33
lines changed

pom.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
</parent>
88

99
<artifactId>vsphere-cloud</artifactId>
10-
<version>1.1.13-SNAPSHOT</version>
10+
<version>2.0-SNAPSHOT</version>
1111
<packaging>hpi</packaging>
1212
<name>vSphere Plugin</name>
1313
<description>Integrates Jenkins with a vSphere server</description>
@@ -65,6 +65,12 @@
6565
<version>2.1-rev7</version>
6666
<type>jar</type>
6767
</dependency>
68+
<dependency>
69+
<groupId>org.jenkins-ci.plugins</groupId>
70+
<artifactId>credentials</artifactId>
71+
<version>1.15</version>
72+
<type>jar</type>
73+
</dependency>
6874
<dependency>
6975
<groupId>com.vmware</groupId>
7076
<artifactId>vijava</artifactId>

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

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55
package org.jenkinsci.plugins;
66

7+
import org.jenkinsci.plugins.vsphere.VSphereConnectionConfig;
78
import hudson.Extension;
89
import hudson.Util;
910
import hudson.model.TaskListener;
@@ -15,15 +16,18 @@
1516
import hudson.slaves.SlaveComputer;
1617
import hudson.util.FormValidation;
1718
import hudson.util.Scrambler;
19+
import java.io.IOException;
1820

1921
import java.util.Collection;
2022
import java.util.Collections;
2123
import java.util.Hashtable;
2224
import java.util.concurrent.ConcurrentHashMap;
2325
import java.util.concurrent.ConcurrentMap;
2426
import java.util.logging.Level;
27+
import javax.annotation.CheckForNull;
2528

2629
import net.sf.json.JSONObject;
30+
import org.apache.commons.lang.StringUtils;
2731

2832
import org.apache.commons.lang.builder.HashCodeBuilder;
2933
import org.jenkinsci.plugins.vsphere.tools.VSphere;
@@ -38,11 +42,16 @@
3842
*/
3943
public class vSphereCloud extends Cloud {
4044

41-
private final String vsHost;
45+
@Deprecated
46+
private transient String vsHost;
4247
private final String vsDescription;
43-
private final String username;
44-
private final String password;
48+
@Deprecated
49+
private transient String username;
50+
@Deprecated
51+
private transient String password;
4552
private final int maxOnlineSlaves;
53+
private VSphereConnectionConfig vsConnectionConfig;
54+
4655
private transient int currentOnlineSlaveCount = 0;
4756
private transient Hashtable<String, String> currentOnline;
4857

@@ -85,19 +94,28 @@ public static void Log(SlaveComputer slave, TaskListener listener, String format
8594
InternalLog(null, slave, listener, format, args);
8695
}
8796

88-
@DataBoundConstructor
97+
@Deprecated
8998
public vSphereCloud(String vsHost, String vsDescription,
9099
String username, String password, int maxOnlineSlaves) {
100+
this(null , vsDescription, maxOnlineSlaves);
101+
}
102+
103+
@DataBoundConstructor
104+
public vSphereCloud(VSphereConnectionConfig vsConnectionConfig, String vsDescription, int maxOnlineSlaves) {
91105
super("vSphereCloud");
92-
this.vsHost = vsHost;
93106
this.vsDescription = vsDescription;
94-
this.username = username;
95-
this.password = Scrambler.scramble(Util.fixEmptyAndTrim(password));
96107
this.maxOnlineSlaves = maxOnlineSlaves;
97-
108+
this.vsConnectionConfig = vsConnectionConfig;
98109
Log("STARTING VSPHERE CLOUD");
99110
}
100111

112+
public Object readResolve() throws IOException {
113+
if (vsConnectionConfig == null) {
114+
vsConnectionConfig = new VSphereConnectionConfig(vsHost, null);
115+
}
116+
return this;
117+
}
118+
101119
protected void EnsureLists() {
102120
if (currentOnline == null)
103121
currentOnline = new Hashtable<String, String>();
@@ -107,20 +125,24 @@ public int getMaxOnlineSlaves() {
107125
return maxOnlineSlaves;
108126
}
109127

110-
public String getPassword() {
111-
return Scrambler.descramble(password);
128+
public @CheckForNull String getPassword() {
129+
return vsConnectionConfig != null ? vsConnectionConfig.getPassword() : null;
112130
}
113131

114-
public String getUsername() {
115-
return username;
132+
public @CheckForNull String getUsername() {
133+
return vsConnectionConfig != null ? vsConnectionConfig.getUsername() : null;
116134
}
117135

118136
public String getVsDescription() {
119137
return vsDescription;
120138
}
121139

122140
public String getVsHost() {
123-
return vsHost;
141+
return vsConnectionConfig != null ? vsConnectionConfig.getVsHost(): null;
142+
}
143+
144+
public VSphereConnectionConfig getVsConnectionConfig() {
145+
return vsConnectionConfig;
124146
}
125147

126148
public final int getHash() {
@@ -203,11 +225,11 @@ public DescriptorImpl getDescriptor() {
203225
@Extension
204226
public static final class DescriptorImpl extends Descriptor<Cloud> {
205227

206-
public final ConcurrentMap<String, vSphereCloud> hypervisors = new ConcurrentHashMap<String, vSphereCloud>();
207-
private String vsHost;
208-
private String username;
209-
private String password;
210-
private int maxOnlineSlaves;
228+
public final @Deprecated ConcurrentMap<String, vSphereCloud> hypervisors = new ConcurrentHashMap<String, vSphereCloud>();
229+
private @Deprecated String vsHost;
230+
private @Deprecated String username;
231+
private @Deprecated String password;
232+
private @Deprecated int maxOnlineSlaves;
211233

212234
@Override
213235
public String getDisplayName() {
@@ -230,8 +252,7 @@ public boolean configure(StaplerRequest req, JSONObject o)
230252
*/
231253
public FormValidation doTestConnection(@QueryParameter String vsHost,
232254
@QueryParameter String vsDescription,
233-
@QueryParameter String username,
234-
@QueryParameter String password,
255+
@QueryParameter String credentialsId,
235256
@QueryParameter int maxOnlineSlaves) {
236257
try {
237258
/* We know that these objects are not null */
@@ -246,12 +267,16 @@ else if (vsHost.endsWith("/")) {
246267
return FormValidation.error("vSphere host name must NOT end with a trailing slash");
247268
}
248269
}
249-
250-
if (username.length() == 0) {
270+
271+
final VSphereConnectionConfig config = new VSphereConnectionConfig(vsHost, credentialsId);
272+
final String username = config.getUsername();
273+
final String password = config.getPassword();
274+
275+
if (StringUtils.isEmpty(username)) {
251276
return FormValidation.error("Username is not specified");
252277
}
253278

254-
if (password.length() == 0) {
279+
if (StringUtils.isEmpty(password)) {
255280
return FormValidation.error("Password is not specified");
256281
}
257282

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
* Copyright 2014 Oleg Nenashev <o.v.nenashev@gmail.com>.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.jenkinsci.plugins.vsphere;
18+
19+
import com.cloudbees.plugins.credentials.CredentialsMatcher;
20+
import com.cloudbees.plugins.credentials.CredentialsMatchers;
21+
import com.cloudbees.plugins.credentials.CredentialsProvider;
22+
import com.cloudbees.plugins.credentials.common.StandardCredentials;
23+
import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
24+
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
25+
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
26+
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
27+
import com.cloudbees.plugins.credentials.domains.HostnameRequirement;
28+
import hudson.Extension;
29+
import hudson.Util;
30+
import hudson.model.AbstractDescribableImpl;
31+
import hudson.model.Descriptor;
32+
import hudson.security.ACL;
33+
import hudson.util.FormValidation;
34+
import hudson.util.ListBoxModel;
35+
import hudson.util.Secret;
36+
import javax.annotation.CheckForNull;
37+
import javax.annotation.Nonnull;
38+
import jenkins.model.Jenkins;
39+
import org.kohsuke.stapler.DataBoundConstructor;
40+
import org.kohsuke.stapler.QueryParameter;
41+
42+
/**
43+
*
44+
* @author Oleg Nenashev <o.v.nenashev@gmail.com>
45+
*/
46+
public class VSphereConnectionConfig extends AbstractDescribableImpl<VSphereConnectionConfig> {
47+
48+
private final @CheckForNull String vsHost;
49+
private final @CheckForNull String credentialsId;
50+
51+
@DataBoundConstructor
52+
public VSphereConnectionConfig(String vsHost, String credentialsId) {
53+
this.vsHost = vsHost;
54+
this.credentialsId = credentialsId;
55+
}
56+
57+
public @CheckForNull String getVsHost() {
58+
return vsHost;
59+
}
60+
61+
public @CheckForNull String getCredentialsId() {
62+
return credentialsId;
63+
}
64+
65+
public @CheckForNull StandardCredentials getCredentials() {
66+
return DescriptorImpl.lookupCredentials(credentialsId, vsHost);
67+
}
68+
69+
public @CheckForNull String getPassword() {
70+
StandardCredentials credentials = getCredentials();
71+
72+
if (credentials instanceof StandardUsernamePasswordCredentials) {
73+
final Secret password = ((StandardUsernamePasswordCredentials)credentials).getPassword();
74+
return Secret.toString(password);
75+
}
76+
return null;
77+
}
78+
79+
public @CheckForNull String getUsername() {
80+
StandardCredentials credentials = getCredentials();
81+
82+
if (credentials instanceof StandardUsernameCredentials) {
83+
return ((StandardUsernameCredentials)credentials).getUsername();
84+
}
85+
return null;
86+
}
87+
88+
@Extension
89+
public static class DescriptorImpl extends Descriptor<VSphereConnectionConfig> {
90+
91+
@Override
92+
public String getDisplayName() {
93+
return "N/A";
94+
}
95+
96+
public ListBoxModel doFillCredentialsIdItems( @QueryParameter String vsHost) {
97+
final Jenkins instance = Jenkins.getInstance();
98+
if (instance != null && instance.hasPermission(Jenkins.ADMINISTER)) {
99+
return new StandardListBoxModel().withEmptySelection().withMatching(
100+
CREDENTIALS_MATCHER, CredentialsProvider.lookupCredentials(StandardCredentials.class,
101+
instance, ACL.SYSTEM, getDomainRequirement(vsHost))
102+
);
103+
} else {
104+
return new StandardListBoxModel();
105+
}
106+
}
107+
108+
public FormValidation doCheckCredentialsId(@QueryParameter String vsHost,
109+
@QueryParameter String value) {
110+
final Jenkins instance = Jenkins.getInstance();
111+
if (instance != null && instance.hasPermission(Jenkins.ADMINISTER)) {
112+
// nop
113+
} else {
114+
return FormValidation.ok();
115+
}
116+
117+
value = Util.fixEmptyAndTrim(value);
118+
if (value == null) {
119+
return FormValidation.ok();
120+
}
121+
122+
vsHost = Util.fixEmptyAndTrim(vsHost);
123+
if (vsHost == null) {
124+
return FormValidation.warning("Cannot validate credentials. Host is not set");
125+
}
126+
127+
final StandardCredentials credentials = lookupCredentials(value, vsHost);
128+
if (credentials == null) {
129+
return FormValidation.warning("Cannot find any credentials with id " + value);
130+
}
131+
132+
return FormValidation.ok();
133+
}
134+
135+
// Support on login/password authentication
136+
private static final CredentialsMatcher CREDENTIALS_MATCHER = CredentialsMatchers.anyOf(
137+
CredentialsMatchers.instanceOf(StandardUsernamePasswordCredentials.class)
138+
);
139+
140+
private static @Nonnull DomainRequirement getDomainRequirement(String hostname) {
141+
return new HostnameRequirement(hostname);
142+
}
143+
144+
public static @CheckForNull StandardCredentials lookupCredentials
145+
(@CheckForNull String credentialsId, @Nonnull String vsHost) {
146+
final Jenkins instance = Jenkins.getInstance();
147+
if (instance != null && credentialsId != null) {
148+
return CredentialsMatchers.firstOrNull(
149+
CredentialsProvider.lookupCredentials(StandardCredentials.class, instance,
150+
ACL.SYSTEM, getDomainRequirement(vsHost)),
151+
CredentialsMatchers.withId(credentialsId));
152+
}
153+
return null;
154+
}
155+
}
156+
}

src/main/resources/org/jenkinsci/plugins/vSphereCloud/config.jelly

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
1-
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
2-
<f:entry title="${%vSphere Host}" field="vsHost">
3-
<f:textbox />
1+
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
2+
<f:entry>
3+
<f:property field="vsConnectionConfig"/>
44
</f:entry>
55
<f:entry title="${%Name to use for this vSphere Cloud}" field="vsDescription">
66
<f:textbox />
77
</f:entry>
8-
<f:entry title="${%Username}" field="username">
9-
<f:textbox />
10-
</f:entry>
11-
<f:entry title="${%Password}" field="password">
12-
<f:password />
13-
</f:entry>
148

159
<f:advanced>
1610
<f:entry title="${%Max number of slaves online}" field="maxOnlineSlaves">
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.jenkinsci.plugins.vsphere.VSphereConnectionConfig;
2+
3+
f = namespace(lib.FormTagLib)
4+
c = namespace(lib.CredentialsTagLib)
5+
6+
f.entry(title:_("vSphere Host"), field:"vsHost") {
7+
f.textbox()
8+
}
9+
10+
f.entry(title:_("Credentials"), field:"credentialsId") {
11+
c.select(onchange="""{
12+
var self = this.targetElement ? this.targetElement : this;
13+
var r = findPreviousFormItem(self,'url');
14+
r.onchange(r);
15+
self = null;
16+
r = null;
17+
}""" /* workaround for JENKINS-19124 */)
18+
}

src/main/resources/org/jenkinsci/plugins/vSphereCloud/help-vsHost.html renamed to src/main/resources/org/jenkinsci/plugins/vsphere/VSphereConnectionConfig/help-vsHost.html

File renamed without changes.

0 commit comments

Comments
 (0)