From 86319de87b8469c06cf56ed933cfe85bade4eee4 Mon Sep 17 00:00:00 2001 From: ikedam Date: Sun, 13 Apr 2014 08:48:09 +0900 Subject: [PATCH 001/978] [FIXED JENKINS-19565] Pass ${it} and ${instance} to contents of dropdownDescriptorSelector. And developers now can specify attr.capture to pass any variables. --- .../resources/lib/form/dropdownDescriptorSelector.jelly | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/src/main/resources/lib/form/dropdownDescriptorSelector.jelly b/core/src/main/resources/lib/form/dropdownDescriptorSelector.jelly index aad0a87e51..122e7f6a8b 100644 --- a/core/src/main/resources/lib/form/dropdownDescriptorSelector.jelly +++ b/core/src/main/resources/lib/form/dropdownDescriptorSelector.jelly @@ -42,6 +42,11 @@ THE SOFTWARE. If specified, this will be chosen as the default value in case the current selection is null. The default can be an specific instance or a descriptor e.g. ${descriptor.defaultSettingsProvider} or ${descriptor.defaultSettingsProvider.descriptor}. In the later case, the from input fields will be empty. + + Config fragments from descriptors are rendered lazily by default, which means + variables seen in the caller aren't visible to them. This attribute allows you + to nominate additional variables and their values to be captured for descriptors. + @@ -51,10 +56,11 @@ THE SOFTWARE. + + lazy="descriptor,it,${capture}"> -- GitLab From 358b9cee87c154e7ed9b689acfc35bf58b481614 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Fri, 27 Jun 2014 15:25:40 -0700 Subject: [PATCH 002/978] Adding a module to allow organizations to track usage of Jenkins within their domain. There is a privacy concern in having this information reported, but OTOH in a corporate network this seems like a reasonable compromise, and people really often do not have any clues where they are running. --- war/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/war/pom.xml b/war/pom.xml index d4d10cd1b0..6179ef6247 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -106,6 +106,11 @@ THE SOFTWARE. 2.3 test + + org.jenkins-ci.modules + domain-discovery + 1.0 + org.jenkins-ci.modules instance-identity -- GitLab From 9bb35a78b8298930adf978046e9f6c53c6853e27 Mon Sep 17 00:00:00 2001 From: Akshay Dayal Date: Fri, 17 Apr 2015 08:11:43 -0700 Subject: [PATCH 003/978] [JENKINS-26580] Initial implementation of JNLP3-connect protocol --- .../slaves/JnlpSlaveAgentProtocol3.java | 221 ++++++++++++++++++ .../jenkins/slaves/JnlpSlaveHandshake.java | 2 +- pom.xml | 2 +- 3 files changed, 223 insertions(+), 2 deletions(-) create mode 100644 core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol3.java diff --git a/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol3.java b/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol3.java new file mode 100644 index 0000000000..5c4d348b00 --- /dev/null +++ b/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol3.java @@ -0,0 +1,221 @@ +package jenkins.slaves; + +import hudson.AbortException; +import hudson.Extension; +import hudson.TcpSlaveAgentListener; +import hudson.Util; +import hudson.remoting.Channel; +import hudson.remoting.ChannelBuilder; +import hudson.remoting.SocketChannelStream; +import hudson.slaves.SlaveComputer; +import jenkins.AgentProtocol; +import jenkins.model.Jenkins; +import org.jenkinsci.remoting.engine.JnlpProtocol; +import org.jenkinsci.remoting.engine.JnlpProtocol3; +import org.jenkinsci.remoting.engine.jnlp3.ChannelCiphers; +import org.jenkinsci.remoting.engine.jnlp3.CipherUtils; +import org.jenkinsci.remoting.engine.jnlp3.HandshakeCiphers; +import org.jenkinsci.remoting.nio.NioChannelHub; + +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import javax.inject.Inject; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.Socket; +import java.security.SecureRandom; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.logging.Level; + +/** + * Master-side implementation for JNLP3-connect protocol. + * + *

@see {@link JnlpProtocol3} for more details. + */ +@Extension +public class JnlpSlaveAgentProtocol3 extends AgentProtocol { + @Inject + NioChannelSelector hub; + + @Override + public String getName() { + return "JNLP3-connect"; + } + + @Override + public void handle(Socket socket) throws IOException, InterruptedException { + new Handler(hub.getHub(), socket).run(); + } + + static class Handler extends JnlpSlaveHandshake { + + public Handler(NioChannelHub hub, Socket socket) throws IOException { + super(hub,socket, + new DataInputStream(socket.getInputStream()), + new PrintWriter(new BufferedWriter( + new OutputStreamWriter(socket.getOutputStream(), "UTF-8")), true)); + } + + protected void run() throws IOException, InterruptedException { + request.load(new ByteArrayInputStream(in.readUTF().getBytes("UTF-8"))); + String nodeName = request.getProperty(JnlpProtocol3.SLAVE_NAME_KEY); + String encryptedChallenge = request.getProperty(JnlpProtocol3.CHALLENGE_KEY); + byte[] handshakeSpecKey = CipherUtils.keyFromString( + request.getProperty(JnlpProtocol3.HANDSHAKE_SPEC_KEY)); + String cookie = request.getProperty(JnlpProtocol3.COOKIE_KEY); + + SlaveComputer computer = (SlaveComputer) Jenkins.getInstance().getComputer(nodeName); + if(computer == null) { + error("Slave trying to register for invalid node: " + nodeName); + return; + } + String slaveSecret = computer.getJnlpMac(); + + HandshakeCiphers handshakeCiphers = null; + try { + handshakeCiphers = HandshakeCiphers.create(nodeName, slaveSecret, handshakeSpecKey); + } catch (Exception e) { + error("Failed to create handshake ciphers for node: " + nodeName); + return; + } + + String challenge = null; + try { + challenge = handshakeCiphers.decrypt(encryptedChallenge); + } catch (Exception e) { + throw new IOException("Unable to decrypt challenge", e); + } + if (!challenge.startsWith(JnlpProtocol3.CHALLENGE_PREFIX)) { + error("Received invalid challenge"); + return; + } + + // At this point the slave looks legit, check if we think they are already connected. + Channel oldChannel = computer.getChannel(); + if(oldChannel != null) { + if (cookie != null && cookie.equals(oldChannel.getProperty(COOKIE_NAME))) { + // We think we are currently connected, but this request proves that it's from + // the party we are supposed to be communicating to. so let the current one get + // disconnected + LOGGER.info("Disconnecting " + nodeName + + " as we are reconnected from the current peer"); + try { + computer.disconnect(new TcpSlaveAgentListener.ConnectionFromCurrentPeer()) + .get(15, TimeUnit.SECONDS); + } catch (ExecutionException e) { + throw new IOException("Failed to disconnect the current client",e); + } catch (TimeoutException e) { + throw new IOException("Failed to disconnect the current client",e); + } + } else { + error(nodeName + + " is already connected to this master. Rejecting this connection."); + return; + } + } + + // Send challenge response. + String challengeReverse = new StringBuilder( + challenge.substring(JnlpProtocol3.CHALLENGE_PREFIX.length())) + .reverse().toString(); + String challengeResponse = JnlpProtocol3.CHALLENGE_PREFIX + challengeReverse; + String encryptedChallengeResponse = null; + try { + encryptedChallengeResponse = handshakeCiphers.encrypt(challengeResponse); + } catch (Exception e) { + throw new IOException("Error encrypting challenge response", e); + } + out.println(encryptedChallengeResponse.getBytes("UTF-8").length); + out.print(encryptedChallengeResponse); + out.flush(); + + // If the slave accepted our challenge response it will send channel cipher keys. + String challengeVerificationMessage = in.readUTF(); + if (!challengeVerificationMessage.equals(JnlpProtocol.GREETING_SUCCESS)) { + error("Slave did not accept our challenge response"); + return; + } + String encryptedAesKeyString = in.readUTF(); + String encryptedSpecKeyString = in.readUTF(); + ChannelCiphers channelCiphers = null; + try { + String aesKeyString = handshakeCiphers.decrypt(encryptedAesKeyString); + String specKeyString = handshakeCiphers.decrypt(encryptedSpecKeyString); + channelCiphers = ChannelCiphers.create( + CipherUtils.keyFromString(aesKeyString), + CipherUtils.keyFromString(specKeyString)); + } catch (Exception e) { + error("Failed to decrypt channel cipher keys"); + return; + } + + String newCookie = generateCookie(); + try { + out.println(handshakeCiphers.encrypt(newCookie)); + } catch (Exception e) { + throw new IOException("Error encrypting cookie", e); + } + + Channel establishedChannel = jnlpConnect(computer, channelCiphers); + establishedChannel.setProperty(COOKIE_NAME, newCookie); + } + + protected Channel jnlpConnect( + SlaveComputer computer, ChannelCiphers channelCiphers) + throws InterruptedException, IOException { + final String nodeName = computer.getName(); + final OutputStream log = computer.openLogFile(); + PrintWriter logw = new PrintWriter(log,true); + logw.println("JNLP agent connected from "+ socket.getInetAddress()); + + try { + ChannelBuilder cb = createChannelBuilder(nodeName); + Channel channel = cb.withHeaderStream(log) + .build(new CipherInputStream(SocketChannelStream.in(socket), + channelCiphers.getDecryptCipher()), + new CipherOutputStream(SocketChannelStream.out(socket), + channelCiphers.getEncryptCipher())); + + computer.setChannel(channel, log, + new Channel.Listener() { + @Override + public void onClosed(Channel channel, IOException cause) { + if(cause != null) + LOGGER.log(Level.WARNING, + Thread.currentThread().getName() + " for + " + + nodeName + " terminated", cause); + try { + socket.close(); + } catch (IOException e) { + // Do nothing. + } + } + }); + return computer.getChannel(); + } catch (AbortException e) { + logw.println(e.getMessage()); + logw.println("Failed to establish the connection with the slave"); + throw e; + } catch (IOException e) { + logw.println("Failed to establish the connection with the slave " + nodeName); + e.printStackTrace(logw); + throw e; + } + } + + private String generateCookie() { + byte[] cookie = new byte[32]; + new SecureRandom().nextBytes(cookie); + return Util.toHexString(cookie); + } + } + + static final String COOKIE_NAME = JnlpSlaveAgentProtocol3.class.getName() + ".cookie"; +} diff --git a/core/src/main/java/jenkins/slaves/JnlpSlaveHandshake.java b/core/src/main/java/jenkins/slaves/JnlpSlaveHandshake.java index b7c01a239e..eb9499e1c3 100644 --- a/core/src/main/java/jenkins/slaves/JnlpSlaveHandshake.java +++ b/core/src/main/java/jenkins/slaves/JnlpSlaveHandshake.java @@ -114,5 +114,5 @@ public class JnlpSlaveHandshake { } - private static final Logger LOGGER = Logger.getLogger(JnlpSlaveHandshake.class.getName()); + static final Logger LOGGER = Logger.getLogger(JnlpSlaveHandshake.class.getName()); } diff --git a/pom.xml b/pom.xml index bb2e89b46a..4a525155a5 100644 --- a/pom.xml +++ b/pom.xml @@ -176,7 +176,7 @@ THE SOFTWARE. org.jenkins-ci.main remoting - 2.50 + 2.51-SNAPSHOT -- GitLab From e9f5caa13fa1a3d1bf602ec9d67dac75f3310889 Mon Sep 17 00:00:00 2001 From: Akshay Dayal Date: Thu, 23 Apr 2015 02:24:40 -0700 Subject: [PATCH 004/978] [JENKINS-26580] Updated implementation of Jnlp3 protocol --- .../slaves/JnlpSlaveAgentProtocol3.java | 119 ++++++++++-------- 1 file changed, 64 insertions(+), 55 deletions(-) diff --git a/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol3.java b/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol3.java index 5c4d348b00..ce0a59beef 100644 --- a/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol3.java +++ b/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol3.java @@ -13,8 +13,8 @@ import jenkins.model.Jenkins; import org.jenkinsci.remoting.engine.JnlpProtocol; import org.jenkinsci.remoting.engine.JnlpProtocol3; import org.jenkinsci.remoting.engine.jnlp3.ChannelCiphers; -import org.jenkinsci.remoting.engine.jnlp3.CipherUtils; import org.jenkinsci.remoting.engine.jnlp3.HandshakeCiphers; +import org.jenkinsci.remoting.engine.jnlp3.Jnlp3Util; import org.jenkinsci.remoting.nio.NioChannelHub; import javax.crypto.CipherInputStream; @@ -28,6 +28,7 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; +import java.nio.charset.Charset; import java.security.SecureRandom; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -38,6 +39,8 @@ import java.util.logging.Level; * Master-side implementation for JNLP3-connect protocol. * *

@see {@link JnlpProtocol3} for more details. + * + * @author Akshay Dayal */ @Extension public class JnlpSlaveAgentProtocol3 extends AgentProtocol { @@ -59,45 +62,41 @@ public class JnlpSlaveAgentProtocol3 extends AgentProtocol { public Handler(NioChannelHub hub, Socket socket) throws IOException { super(hub,socket, new DataInputStream(socket.getInputStream()), - new PrintWriter(new BufferedWriter( - new OutputStreamWriter(socket.getOutputStream(), "UTF-8")), true)); + new PrintWriter(new BufferedWriter(new OutputStreamWriter( + socket.getOutputStream(), Charset.forName("UTF-8"))), true)); } protected void run() throws IOException, InterruptedException { - request.load(new ByteArrayInputStream(in.readUTF().getBytes("UTF-8"))); + // Get initiation information from slave. + request.load(new ByteArrayInputStream(in.readUTF().getBytes(Charset.forName("UTF-8")))); String nodeName = request.getProperty(JnlpProtocol3.SLAVE_NAME_KEY); - String encryptedChallenge = request.getProperty(JnlpProtocol3.CHALLENGE_KEY); - byte[] handshakeSpecKey = CipherUtils.keyFromString( - request.getProperty(JnlpProtocol3.HANDSHAKE_SPEC_KEY)); - String cookie = request.getProperty(JnlpProtocol3.COOKIE_KEY); + // Create handshake ciphers. SlaveComputer computer = (SlaveComputer) Jenkins.getInstance().getComputer(nodeName); if(computer == null) { error("Slave trying to register for invalid node: " + nodeName); return; } String slaveSecret = computer.getJnlpMac(); + HandshakeCiphers handshakeCiphers = HandshakeCiphers.create(nodeName, slaveSecret); - HandshakeCiphers handshakeCiphers = null; - try { - handshakeCiphers = HandshakeCiphers.create(nodeName, slaveSecret, handshakeSpecKey); - } catch (Exception e) { - error("Failed to create handshake ciphers for node: " + nodeName); + // Authenticate to the slave. + if (!authenticateToSlave(handshakeCiphers)) { return; } - String challenge = null; - try { - challenge = handshakeCiphers.decrypt(encryptedChallenge); - } catch (Exception e) { - throw new IOException("Unable to decrypt challenge", e); + // If there is a cookie decrypt it. + String cookie = null; + if (request.getProperty(JnlpProtocol3.COOKIE_KEY) != null) { + cookie = handshakeCiphers.decrypt(request.getProperty(JnlpProtocol3.COOKIE_KEY)); } - if (!challenge.startsWith(JnlpProtocol3.CHALLENGE_PREFIX)) { - error("Received invalid challenge"); + + // Validate the slave. + if (!validateSlave(handshakeCiphers)) { return; } - // At this point the slave looks legit, check if we think they are already connected. + // The slave is authenticated, see if its already connected. Channel oldChannel = computer.getChannel(); if(oldChannel != null) { if (cookie != null && cookie.equals(oldChannel.getProperty(COOKIE_NAME))) { @@ -121,50 +120,60 @@ public class JnlpSlaveAgentProtocol3 extends AgentProtocol { } } - // Send challenge response. - String challengeReverse = new StringBuilder( - challenge.substring(JnlpProtocol3.CHALLENGE_PREFIX.length())) - .reverse().toString(); - String challengeResponse = JnlpProtocol3.CHALLENGE_PREFIX + challengeReverse; - String encryptedChallengeResponse = null; - try { - encryptedChallengeResponse = handshakeCiphers.encrypt(challengeResponse); - } catch (Exception e) { - throw new IOException("Error encrypting challenge response", e); - } - out.println(encryptedChallengeResponse.getBytes("UTF-8").length); + // Send greeting and new cookie. + out.println(JnlpProtocol.GREETING_SUCCESS); + String newCookie = generateCookie(); + out.println(handshakeCiphers.encrypt(newCookie)); + + // Now get the channel cipher information. + String aesKeyString = handshakeCiphers.decrypt(in.readUTF()); + String specKeyString = handshakeCiphers.decrypt(in.readUTF()); + ChannelCiphers channelCiphers = ChannelCiphers.create( + Jnlp3Util.keyFromString(aesKeyString), + Jnlp3Util.keyFromString(specKeyString)); + + Channel establishedChannel = jnlpConnect(computer, channelCiphers); + establishedChannel.setProperty(COOKIE_NAME, newCookie); + } + + private boolean authenticateToSlave(HandshakeCiphers handshakeCiphers) throws IOException { + String challenge = handshakeCiphers.decrypt( + request.getProperty(JnlpProtocol3.CHALLENGE_KEY)); + + // Send slave challenge response. + String challengeResponse = Jnlp3Util.createChallengeResponse(challenge); + String encryptedChallengeResponse = handshakeCiphers.encrypt(challengeResponse); + out.println(encryptedChallengeResponse.getBytes(Charset.forName("UTF-8")).length); out.print(encryptedChallengeResponse); out.flush(); - // If the slave accepted our challenge response it will send channel cipher keys. + // If the slave accepted our challenge response send our challenge. String challengeVerificationMessage = in.readUTF(); if (!challengeVerificationMessage.equals(JnlpProtocol.GREETING_SUCCESS)) { error("Slave did not accept our challenge response"); - return; - } - String encryptedAesKeyString = in.readUTF(); - String encryptedSpecKeyString = in.readUTF(); - ChannelCiphers channelCiphers = null; - try { - String aesKeyString = handshakeCiphers.decrypt(encryptedAesKeyString); - String specKeyString = handshakeCiphers.decrypt(encryptedSpecKeyString); - channelCiphers = ChannelCiphers.create( - CipherUtils.keyFromString(aesKeyString), - CipherUtils.keyFromString(specKeyString)); - } catch (Exception e) { - error("Failed to decrypt channel cipher keys"); - return; + return false; } - String newCookie = generateCookie(); - try { - out.println(handshakeCiphers.encrypt(newCookie)); - } catch (Exception e) { - throw new IOException("Error encrypting cookie", e); + return true; + } + + private boolean validateSlave(HandshakeCiphers handshakeCiphers) throws IOException { + String masterChallenge = Jnlp3Util.generateChallenge(); + String encryptedMasterChallenge = handshakeCiphers.encrypt(masterChallenge); + out.println(encryptedMasterChallenge.getBytes(Charset.forName("UTF-8")).length); + out.print(encryptedMasterChallenge); + out.flush(); + + // Verify the challenge response from the slave. + String encryptedMasterChallengeResponse = in.readUTF(); + String masterChallengeResponse = handshakeCiphers.decrypt( + encryptedMasterChallengeResponse); + if (!Jnlp3Util.validateChallengeResponse(masterChallenge, masterChallengeResponse)) { + error("Incorrect master challenge response from slave"); + return false; } - Channel establishedChannel = jnlpConnect(computer, channelCiphers); - establishedChannel.setProperty(COOKIE_NAME, newCookie); + return true; } protected Channel jnlpConnect( -- GitLab From 65f88cce61aa006f2d2d63fa5f75a0b87c1f180b Mon Sep 17 00:00:00 2001 From: Daniel Spilker Date: Mon, 26 Oct 2015 20:50:05 +0100 Subject: [PATCH 005/978] allow @WithPlugin to install multiple plugins --- .../jvnet/hudson/test/recipes/WithPlugin.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/test/src/main/java/org/jvnet/hudson/test/recipes/WithPlugin.java b/test/src/main/java/org/jvnet/hudson/test/recipes/WithPlugin.java index 9700e17851..1dfafea490 100644 --- a/test/src/main/java/org/jvnet/hudson/test/recipes/WithPlugin.java +++ b/test/src/main/java/org/jvnet/hudson/test/recipes/WithPlugin.java @@ -39,7 +39,7 @@ import java.lang.annotation.Target; import java.net.URL; /** - * Installs the specified plugin before launching Hudson. + * Installs the specified plugins before launching Jenkins. * * @author Kohsuke Kawaguchi */ @@ -50,9 +50,9 @@ import java.net.URL; @Retention(RUNTIME) public @interface WithPlugin { /** - * Name of the plugin. + * Whitespace separated list of plugin names. * - * For now, this has to be one of the plugins statically available in resources + * For now, this has to be one or more of the plugins statically available in resources * "/plugins/NAME". TODO: support retrieval through Maven repository. * TODO: load the HPI file from $M2_REPO or $USER_HOME/.m2 by naming e.g. org.jvnet.hudson.plugins:monitoring:hpi:1.34.0 * (used in conjunction with the depepdency in POM to ensure it's available) @@ -70,8 +70,10 @@ public @interface WithPlugin { @Override public void decorateHome(HudsonTestCase testCase, File home) throws Exception { - URL res = getClass().getClassLoader().getResource("plugins/" + a.value()); - FileUtils.copyURLToFile(res,new File(home,"plugins/"+a.value())); + for (String plugin : a.value().split("\\s+")) { + URL res = getClass().getClassLoader().getResource("plugins/" + plugin); + FileUtils.copyURLToFile(res, new File(home, "plugins/" + plugin)); + } } } @@ -86,8 +88,10 @@ public @interface WithPlugin { @Override public void decorateHome(JenkinsRule jenkinsRule, File home) throws Exception { - URL res = getClass().getClassLoader().getResource("plugins/" + a.value()); - FileUtils.copyURLToFile(res,new File(home,"plugins/"+a.value())); + for (String plugin : a.value().split("\\s+")) { + URL res = getClass().getClassLoader().getResource("plugins/" + plugin); + FileUtils.copyURLToFile(res, new File(home, "plugins/" + plugin)); + } } } } -- GitLab From 18d4a29aa47f37481d53614eefaa624ade60faf6 Mon Sep 17 00:00:00 2001 From: Daniel Spilker Date: Tue, 27 Oct 2015 09:24:18 +0100 Subject: [PATCH 006/978] changed regex to parse CSV --- .../main/java/org/jvnet/hudson/test/recipes/WithPlugin.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/src/main/java/org/jvnet/hudson/test/recipes/WithPlugin.java b/test/src/main/java/org/jvnet/hudson/test/recipes/WithPlugin.java index 1dfafea490..7d019f78fc 100644 --- a/test/src/main/java/org/jvnet/hudson/test/recipes/WithPlugin.java +++ b/test/src/main/java/org/jvnet/hudson/test/recipes/WithPlugin.java @@ -50,7 +50,7 @@ import java.net.URL; @Retention(RUNTIME) public @interface WithPlugin { /** - * Whitespace separated list of plugin names. + * Comma separated list of plugin names. * * For now, this has to be one or more of the plugins statically available in resources * "/plugins/NAME". TODO: support retrieval through Maven repository. @@ -70,7 +70,7 @@ public @interface WithPlugin { @Override public void decorateHome(HudsonTestCase testCase, File home) throws Exception { - for (String plugin : a.value().split("\\s+")) { + for (String plugin : a.value().split("\\s*,\\s*")) { URL res = getClass().getClassLoader().getResource("plugins/" + plugin); FileUtils.copyURLToFile(res, new File(home, "plugins/" + plugin)); } @@ -88,7 +88,7 @@ public @interface WithPlugin { @Override public void decorateHome(JenkinsRule jenkinsRule, File home) throws Exception { - for (String plugin : a.value().split("\\s+")) { + for (String plugin : a.value().split("\\s*,\\s*")) { URL res = getClass().getClassLoader().getResource("plugins/" + plugin); FileUtils.copyURLToFile(res, new File(home, "plugins/" + plugin)); } -- GitLab From 8a9546408bef7c34b7d2310bbe303aad5fa807d6 Mon Sep 17 00:00:00 2001 From: Daniel Spilker Date: Tue, 27 Oct 2015 17:38:47 +0100 Subject: [PATCH 007/978] use array instead of CSV --- .../java/org/jvnet/hudson/test/recipes/WithPlugin.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/src/main/java/org/jvnet/hudson/test/recipes/WithPlugin.java b/test/src/main/java/org/jvnet/hudson/test/recipes/WithPlugin.java index 7d019f78fc..d538ce0565 100644 --- a/test/src/main/java/org/jvnet/hudson/test/recipes/WithPlugin.java +++ b/test/src/main/java/org/jvnet/hudson/test/recipes/WithPlugin.java @@ -50,14 +50,14 @@ import java.net.URL; @Retention(RUNTIME) public @interface WithPlugin { /** - * Comma separated list of plugin names. + * Name of the plugins. * * For now, this has to be one or more of the plugins statically available in resources * "/plugins/NAME". TODO: support retrieval through Maven repository. * TODO: load the HPI file from $M2_REPO or $USER_HOME/.m2 by naming e.g. org.jvnet.hudson.plugins:monitoring:hpi:1.34.0 * (used in conjunction with the depepdency in POM to ensure it's available) */ - String value(); + String[] value(); class RunnerImpl extends Recipe.Runner { private WithPlugin a; @@ -70,7 +70,7 @@ public @interface WithPlugin { @Override public void decorateHome(HudsonTestCase testCase, File home) throws Exception { - for (String plugin : a.value().split("\\s*,\\s*")) { + for (String plugin : a.value()) { URL res = getClass().getClassLoader().getResource("plugins/" + plugin); FileUtils.copyURLToFile(res, new File(home, "plugins/" + plugin)); } @@ -88,7 +88,7 @@ public @interface WithPlugin { @Override public void decorateHome(JenkinsRule jenkinsRule, File home) throws Exception { - for (String plugin : a.value().split("\\s*,\\s*")) { + for (String plugin : a.value()) { URL res = getClass().getClassLoader().getResource("plugins/" + plugin); FileUtils.copyURLToFile(res, new File(home, "plugins/" + plugin)); } -- GitLab From cc932c49afa8c2946a32555285e1aa52dc724ddf Mon Sep 17 00:00:00 2001 From: Stephen Connolly Date: Wed, 28 Oct 2015 09:32:11 +0000 Subject: [PATCH 008/978] [FIXED JENKINS-31219] A CloudProvisioningListener can prevent provisioning of all clouds instead of just the targeted cloud --- core/src/main/java/hudson/slaves/NodeProvisioner.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/hudson/slaves/NodeProvisioner.java b/core/src/main/java/hudson/slaves/NodeProvisioner.java index 216c8b3ef1..3f5ea3642b 100644 --- a/core/src/main/java/hudson/slaves/NodeProvisioner.java +++ b/core/src/main/java/hudson/slaves/NodeProvisioner.java @@ -690,11 +690,10 @@ public class NodeProvisioner { int workloadToProvision = (int) Math.round(Math.floor(excessWorkload + m)); - for (CloudProvisioningListener cl : CloudProvisioningListener.all()) - // consider displaying reasons in a future cloud ux - { + for (CloudProvisioningListener cl : CloudProvisioningListener.all()) { if (cl.canProvision(c, state.getLabel(), workloadToProvision) != null) { - break CLOUD; + // consider displaying reasons in a future cloud ux + continue CLOUD; } } -- GitLab From d5d2e6e4c5ef3820a4a0bf98e90a9f74f08cb01e Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 9 Dec 2015 08:20:33 -0800 Subject: [PATCH 009/978] [maven-release-plugin] prepare release jenkins-1.625.3 --- cli/pom.xml | 2 +- core/pom.xml | 2 +- plugins/pom.xml | 10 +++++----- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index 6c2baecf78..c728a7e0c5 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main pom - 1.625.3-SNAPSHOT + 1.625.3 cli diff --git a/core/pom.xml b/core/pom.xml index 6d18687202..faa20b575c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 1.625.3-SNAPSHOT + 1.625.3 jenkins-core diff --git a/plugins/pom.xml b/plugins/pom.xml index ee8ba06083..4d69e54ac8 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -12,7 +12,7 @@ org.jenkins-ci.plugins plugin Jenkins plugin POM - 1.625.3-SNAPSHOT + 1.625.3 pom java - -Xrs -Xmx256m -Dhudson.lifecycle=hudson.lifecycle.WindowsServiceLifecycle -jar "%BASE%\jenkins.war" --httpPort=8080 + -Xrs -Xmx256m -Dhudson.lifecycle=hudson.lifecycle.WindowsServiceLifecycle -jar "%BASE%\jenkins.war" --httpPort=8080 --webroot="%BASE%\war"

+

What's new in 1.642 (2015/12/13)

  • Various kinds of settings could not be saved since 1.640. (issue 31954)
-

What's new in 1.641 (2015/12/09)