Skip to content

Commit

Permalink
improve how agents are token offline and online
Browse files Browse the repository at this point in the history
[JENKINS-72284] when an agent with wrong java version connects, it will
be taken offline right away. Before the agent was only taken offline
when one visited the /computer page.
Use monitor specific offline causes that inherit from
MonitorOfflineCause. This allows to take the agent automatically online
again, after it was restarted with the correct java/remoting version.
Also with jenkinsci/jenkins#8618 it would then
reset the Admin monitor if that was the only reason that it fired.

Requires jenkinsci/jenkins#8593 that made 2
mthods public.
  • Loading branch information
mawinter69 committed Nov 19, 2023
1 parent 9b3b70d commit 54edbab
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 39 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
<changelist>999999-SNAPSHOT</changelist>
<gitHubRepo>jenkinsci/${project.artifactId}-plugin</gitHubRepo>
<!-- https://www.jenkins.io/doc/developer/plugin-development/choosing-jenkins-baseline/ -->
<jenkins.version>2.401.3</jenkins.version>
<jenkins.version>2.432-rc34399.5ceb_1a_d95f81</jenkins.version>
<spotbugs.effort>Max</spotbugs.effort>
<spotbugs.threshold>Low</spotbugs.threshold>
<spotless.check.skip>false</spotless.check.skip>
Expand Down
115 changes: 78 additions & 37 deletions src/main/java/hudson/plugin/versioncolumn/JVMVersionMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,22 @@
import hudson.Extension;
import hudson.Util;
import hudson.model.Computer;
import hudson.model.ComputerSet;
import hudson.node_monitors.AbstractAsyncNodeMonitorDescriptor;
import hudson.node_monitors.AbstractDiskSpaceMonitor;
import hudson.node_monitors.DiskSpaceMonitorDescriptor;
import hudson.node_monitors.MonitorOfflineCause;
import hudson.node_monitors.NodeMonitor;
import hudson.remoting.Callable;
import hudson.slaves.OfflineCause;
import hudson.util.ListBoxModel;
import java.io.IOException;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.security.MasterToSlaveCallable;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.export.Exported;

public class JVMVersionMonitor extends NodeMonitor {

Expand Down Expand Up @@ -73,45 +79,64 @@ public String toHtml(String version) {
return version;
}

@Override
public Object data(Computer c) {

String agentVersionStr = (String) super.data(c);
if (agentVersionStr == null) {
return "N/A";
}
Runtime.Version agentVersion;
try {
agentVersion = Runtime.Version.parse(agentVersionStr);
} catch (IllegalArgumentException e) {
LOGGER.log(Level.WARNING, "Failed to parse agent version: " + agentVersionStr, e);
return "N/A";
}
final JVMVersionComparator jvmVersionComparator =
new JVMVersionComparator(CONTROLLER_VERSION, agentVersion, comparisonMode);

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

public JVMVersionComparator.ComparisonMode getComparisonMode() {
return comparisonMode;
}

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

@Override
protected Map<Computer, String> monitor() throws InterruptedException {
Result<String> base = monitorDetailed();
Map<Computer, String> data = base.getMonitoringData();
JVMVersionMonitor monitor = (JVMVersionMonitor) ComputerSet.getMonitors().get(this);
for (Map.Entry<Computer, String> e : data.entrySet()) {
Computer computer = e.getKey();
String version = e.getValue();
if (base.getSkipped().contains(computer)) {
assert version == null;
continue;
}
if (version == null) {
e.setValue(version = get(computer));
}
markNodeOfflineOrOnline(computer, version, monitor);
}
return data;
}

private void markNodeOfflineOrOnline(Computer c, String agentVersionStr, JVMVersionMonitor monitor) {
if (agentVersionStr == null) {
return;
}
Runtime.Version agentVersion;
try {
agentVersion = Runtime.Version.parse(agentVersionStr);
} catch (IllegalArgumentException e) {
LOGGER.log(Level.WARNING, "Failed to parse agent version: " + agentVersionStr, e);
return;
}
final JVMVersionComparator jvmVersionComparator =
new JVMVersionComparator(CONTROLLER_VERSION, agentVersion, monitor.comparisonMode);

if (!isIgnored() && jvmVersionComparator.isNotCompatible()) {
if (monitor.disconnect) {
LOGGER.warning(
Messages.JVMVersionMonitor_MarkedOffline(c.getName(), CONTROLLER_VERSION, agentVersionStr));
markOffline(c, new JVMMismatchCause(Messages.JVMVersionMonitor_OfflineCause()));
} else {
LOGGER.finer("Version incompatibility detected, but keeping the agent '"
+ c.getName()
+ "' online per the node monitor configuration");
}
} else {
if (c.isOffline() && c.getOfflineCause() instanceof JVMMismatchCause) {
markOnline(c);
}
}
}

@Override
@NonNull
public String getDisplayName() {
Expand All @@ -123,11 +148,6 @@ 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);
}

public ListBoxModel doFillComparisonModeItems() {
ListBoxModel items = new ListBoxModel();
for (JVMVersionComparator.ComparisonMode goal : JVMVersionComparator.ComparisonMode.values()) {
Expand All @@ -137,6 +157,27 @@ public ListBoxModel doFillComparisonModeItems() {
}
}

public static class JVMMismatchCause extends MonitorOfflineCause {

private final String message;

public JVMMismatchCause(String message) {
this.message = message;
}

@Override
@Exported(name = "description")
public String toString() {
return message;
}

@NonNull
@Override
public Class<? extends NodeMonitor> getTrigger() {
return JVMVersionMonitor.class;
}
}

private static class JavaVersion extends MasterToSlaveCallable<String, IOException> {
@Override
public String call() {
Expand Down
29 changes: 28 additions & 1 deletion src/main/java/hudson/plugin/versioncolumn/VersionMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import hudson.Util;
import hudson.model.Computer;
import hudson.node_monitors.AbstractNodeMonitorDescriptor;
import hudson.node_monitors.MonitorOfflineCause;
import hudson.node_monitors.NodeMonitor;
import hudson.remoting.Launcher;
import hudson.slaves.OfflineCause;
Expand All @@ -36,6 +37,7 @@
import jenkins.security.MasterToSlaveCallable;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.export.Exported;

public class VersionMonitor extends NodeMonitor {

Expand All @@ -59,9 +61,13 @@ protected String monitor(Computer c) throws IOException, InterruptedException {
String version = c.getChannel().call(new SlaveVersion());
if (version == null || !version.equals(masterVersion)) {
if (!isIgnored()) {
markOffline(c, OfflineCause.create(Messages._VersionMonitor_OfflineCause()));
markOffline(c, new RemotingVersionMismatchCause(Messages.VersionMonitor_OfflineCause()));
LOGGER.warning(Messages.VersionMonitor_MarkedOffline(c.getName()));
}
} else {
if (c.isOffline() && c.getOfflineCause() instanceof RemotingVersionMismatchCause) {
markOnline(c);
}
}
return version;
}
Expand All @@ -77,6 +83,27 @@ public NodeMonitor newInstance(StaplerRequest req, @NonNull JSONObject formData)
}
};

public static class RemotingVersionMismatchCause extends MonitorOfflineCause {

private final String message;

public RemotingVersionMismatchCause(String message) {
this.message = message;
}

@Override
@Exported(name = "description")
public String toString() {
return message;
}

@NonNull
@Override
public Class<? extends NodeMonitor> getTrigger() {
return VersionMonitor.class;
}
}

private static final class SlaveVersion extends MasterToSlaveCallable<String, IOException> {

private static final long serialVersionUID = 1L;
Expand Down

0 comments on commit 54edbab

Please sign in to comment.