Skip to content

Commit

Permalink
[FIX JENKINS-41763] Add JVM version column
Browse files Browse the repository at this point in the history
  • Loading branch information
batmat committed Feb 26, 2017
1 parent 255f1a1 commit b98144c
Show file tree
Hide file tree
Showing 9 changed files with 337 additions and 2 deletions.
17 changes: 16 additions & 1 deletion pom.xml
Expand Up @@ -31,7 +31,22 @@
<jenkins.version>1.625.3</jenkins.version>
<hpi-plugin.version>1.115</hpi-plugin.version>
</properties>


<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>pl.pragmatists</groupId>
<artifactId>JUnitParams</artifactId>
<version>1.0.2</version>
<scope>test</scope>
</dependency>
</dependencies>

<repositories>
<repository>
<id>repo.jenkins-ci.org</id>
Expand Down
@@ -0,0 +1,69 @@
/*
* The MIT License
*
* Copyright (c) 2017-, Baptiste Mathus
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.plugin.versioncolumn;

import com.google.common.annotations.VisibleForTesting;

import javax.annotation.Nonnull;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Responsible for master and agent jvm versions comparisons, and notions of "compatibility".
* <p>For instance, the default behaviour is to consider 1.8.0 compatible with 1.8.3-whatever and so on.
* Only considering <em>major.minor</em> part, that is</p>
*/
public class JVMVersionComparator {
private static final Pattern MAJOR_MINOR_PATTERN = Pattern.compile("(\\d+\\.\\d+).*");
private final String masterVersion;
private final String agentVersion;

public JVMVersionComparator(@Nonnull String masterVersion, String agentVersion, boolean exactMatch) {

if (exactMatch) {
this.masterVersion = masterVersion;
this.agentVersion = agentVersion;
} else {
this.masterVersion = computeMajorMinor(masterVersion);
this.agentVersion = computeMajorMinor(agentVersion);
}
}

@VisibleForTesting
static String computeMajorMinor(String version) {
final Matcher matcher = MAJOR_MINOR_PATTERN.matcher(version);
if (!matcher.matches()) {
throw new IllegalArgumentException(version + "is not a supported JVM version pattern");
}
return matcher.group(1);
}

public boolean isCompatible() {
return masterVersion.equals(agentVersion);
}

public boolean isNotCompatible() {
return !isCompatible();
}
}
120 changes: 120 additions & 0 deletions src/main/java/hudson/plugin/versioncolumn/JVMVersionMonitor.java
@@ -0,0 +1,120 @@
/*
* The MIT License
*
* Copyright (c) 2017-, Baptiste Mathus
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.plugin.versioncolumn;

import hudson.Extension;
import hudson.Util;
import hudson.model.Computer;
import hudson.node_monitors.AbstractAsyncNodeMonitorDescriptor;
import hudson.node_monitors.NodeMonitor;
import hudson.remoting.Callable;
import hudson.slaves.OfflineCause;
import jenkins.security.MasterToSlaveCallable;
import org.kohsuke.stapler.DataBoundConstructor;

import java.io.IOException;
import java.util.logging.Logger;

public class JVMVersionMonitor extends NodeMonitor {

public static final String JAVA_VERSION = "java.version";
private static final String MASTER_VERSION = System.getProperty("java.version");
private static final Logger LOGGER = Logger.getLogger(JVMVersionMonitor.class.getName());

private boolean exactMatch;
private boolean noDisconnect;

@DataBoundConstructor
public JVMVersionMonitor(boolean exactMatch, boolean noDisconnect) {
this.exactMatch = exactMatch;
this.noDisconnect = noDisconnect;
}

public JVMVersionMonitor() {
}

public boolean isExactMatch() {
return exactMatch;
}

public boolean isNoDisconnect() {
return noDisconnect;
}

@SuppressWarnings("unused") // called from column.jelly
public String toHtml(String version) {
if (version != null && !version.equals(MASTER_VERSION)) {
return Util.wrapToErrorSpan(version);
}
return (version == null) ? "" : version;
}

@Override
public Object data(Computer c) {

String agentVersion = (String) super.data(c);
if (agentVersion == null) {
return "N/A";
}
final JVMVersionComparator jvmVersionComparator =
new JVMVersionComparator(MASTER_VERSION, agentVersion, exactMatch);

if (!isIgnored() && jvmVersionComparator.isNotCompatible()) {
if (noDisconnect) {
LOGGER.finer(
"Version incompatibility detected, but keeping the agent '" + c.getName() + "' online per the node monitor configuration");
} else {
LOGGER.warning(Messages.JVMVersionMonitor_MarkedOffline(c.getName(), MASTER_VERSION, agentVersion));
((JvmVersionDescriptor) getDescriptor()).markOffline(c, OfflineCause.create(
Messages._JVMVersionMonitor_OfflineCause()));
}
}
return agentVersion;
}

@Extension
public static class JvmVersionDescriptor extends AbstractAsyncNodeMonitorDescriptor<String> {

public String getDisplayName() {
return Messages.JVMVersionMonitor_DisplayName();
}

@Override
protected Callable<String, IOException> createCallable(Computer c) {
return new JavaVersion();
}

@Override // Just augmenting visibility
public boolean markOffline(Computer c, OfflineCause oc) {
return super.markOffline(c, oc);
}
}

private static class JavaVersion extends MasterToSlaveCallable<String, IOException> {
@Override
public String call() throws IOException {
return System.getProperty(JAVA_VERSION);
}
}
}
@@ -0,0 +1,28 @@
<!--
The MIT License
Copyright (c) 2017-, Baptiste Mathus
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->

<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:s="/lib/form">
<td align="right" data="${data}">${data}</td>
</j:jelly>
@@ -0,0 +1,32 @@
<!--
The MIT License
Copyright (c) 2017-, Baptiste Mathus
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
<f:entry title="${%Exact Match for JVM Version}" field="exactMatch">
<f:checkbox />
</f:entry>
<f:entry title="${%Do NOT disconnect agent when incompatibility is found}" field="noDisconnect">
<f:checkbox />
</f:entry>
</j:jelly>
@@ -0,0 +1,7 @@
<div>
<p>Check this checkbox if you prefer to have a very non-permissive environment with regard to JVM Versions.

<p>The default behaviour of the plugin is to allow 1.8.1_55 to work with 1.8.0_10 for instance, considering
only the two first groups of digits (in that case: <code>1.8</code></p>

</div>
@@ -0,0 +1,7 @@
<div>
<p>Check this checkbox if you prefer to have a very non-permissive environment with regard to JVM Versions.

<p>The default behaviour of the plugin is to allow 1.8.1_55 to work with 1.8.0_10 for instance, considering
only the two first groups of digits (in that case: <code>1.8</code></p>

</div>
@@ -1,3 +1,6 @@
VersionMonitor.DisplayName=Version
VersionMonitor.DisplayName=Agent Version
VersionMonitor.OfflineCause=This node is offline because it uses old slave.jar
VersionMonitor.MarkedOffline=Making {0} offline temporarily due to the use of old slave.jar
JVMVersionMonitor.DisplayName=JVM Version
JVMVersionMonitor.OfflineCause=This node is offline because it uses a different JVM version than Master
JVMVersionMonitor.MarkedOffline=Making {0} offline temporarily due to using a different JVM version (master={1}, agent={2})
@@ -0,0 +1,54 @@
package hudson.plugin.versioncolumn;


import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

@RunWith(JUnitParamsRunner.class)
public class JVMVersionComparatorTest {

@Test
public void computeMajorMinor() {

assertEquals("1.8", JVMVersionComparator.computeMajorMinor("1.8.0"));
assertEquals("1.8", JVMVersionComparator.computeMajorMinor("1.8.0_66"));
assertEquals("1.8", JVMVersionComparator.computeMajorMinor("1.8.1-blah_whatever$wat"));
}

private Object[] parametersForCompatible() {
return new Object[][] {
{"1.8.0", "1.8.0", true },
{"1.8.0", "1.8.0", false },
{"1.6.0", "1.6.0", false},
{"1.6.0", "1.6.1_ublah_whatever", false },
{"1.8.066", "1.8.0110", false },
};
}

private Object[] parametersForIncompatible() {
return new Object[][] {
{"1.5.0", "1.5.1", true },
{"1.7.0", "1.6.0", true },
{"1.8.0_66", "1.8.0_110", true },
{"1.6.0", "1.6.1", true },
};
}
@Test
@Parameters
public void compatible(String masterVersion, String agentVersion, boolean exact) {
assertTrue(new JVMVersionComparator(masterVersion, agentVersion, exact).isCompatible());
}

@Test
@Parameters
public void incompatible(String masterVersion, String agentVersion, boolean exact) {
assertTrue(new JVMVersionComparator(masterVersion, agentVersion, exact).isNotCompatible());
}


}

0 comments on commit b98144c

Please sign in to comment.