Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JENKINS-72009, JENKINS-72200, JENKINS-24947] various fixes and improvements around disk space monitoring #8593

Merged
merged 17 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions core/src/main/java/hudson/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -2299,13 +2299,17 @@ public static String humanReadableByteSize(long size) {
double number = size;
if (number >= 1024) {
number = number / 1024;
measure = "KB";
measure = "KiB";
if (number >= 1024) {
number = number / 1024;
measure = "MB";
measure = "MiB";
if (number >= 1024) {
number = number / 1024;
measure = "GB";
measure = "GiB";
if (number >= 1024) {
number = number / 1024;
measure = "TiB";
}
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions core/src/main/java/hudson/model/Computer.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
import hudson.model.Queue.FlyweightTask;
import hudson.model.labels.LabelAtom;
import hudson.model.queue.WorkUnit;
import hudson.node_monitors.AbstractDiskSpaceMonitor;
import hudson.node_monitors.DiskSpaceMonitorNodeProperty;
import hudson.node_monitors.NodeMonitor;
import hudson.remoting.Channel;
import hudson.remoting.VirtualChannel;
Expand Down Expand Up @@ -1513,6 +1515,14 @@ public void doConfigSubmit(StaplerRequest req, StaplerResponse rsp) throws IOExc
Node result = node.reconfigure(req, req.getSubmittedForm());
Jenkins.get().getNodesObject().replaceNode(this.getNode(), result);

if (result.getNodeProperty(DiskSpaceMonitorNodeProperty.class) != null) {
for (NodeMonitor monitor : NodeMonitor.getAll()) {
if (monitor instanceof AbstractDiskSpaceMonitor) {
monitor.data(this);
}
}
}

// take the user back to the agent top page.
rsp.sendRedirect2("../" + result.getNodeName() + '/');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
import hudson.node_monitors.DiskSpaceMonitorDescriptor.DiskSpace;
import java.text.ParseException;
import java.util.logging.Logger;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.kohsuke.stapler.DataBoundSetter;

/**
* @author Kohsuke Kawaguchi
Expand All @@ -17,54 +18,80 @@ public abstract class AbstractDiskSpaceMonitor extends NodeMonitor {
* This is a human readable string representation as entered by the user, so that we can retain the original notation.
*/
public final String freeSpaceThreshold;
private String freeSpaceWarningThreshold;

protected AbstractDiskSpaceMonitor(String threshold) throws ParseException {
this.freeSpaceThreshold = threshold;
DiskSpace.parse(threshold); // make sure it parses
}

protected AbstractDiskSpaceMonitor() {
this.freeSpaceThreshold = "1GB";
this.freeSpaceThreshold = "1GiB";
Comment on lines -27 to +29
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: this is one of the unusual cases where a settings format change is not forward-compatible. So if you run a controller from a version after this PR and then downgrade to a version prior to it and try to save monitor config, you will get an error

java.lang.NumberFormatException: For input string: "1GI"
	at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2054)
	at java.base/jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
	at java.base/java.lang.Double.parseDouble(Double.java:792)
	at hudson.node_monitors.DiskSpaceMonitorDescriptor$DiskSpace.parse(DiskSpaceMonitorDescriptor.java:163)
	at hudson.node_monitors.AbstractDiskSpaceMonitor.<init>(AbstractDiskSpaceMonitor.java:23)
	at hudson.node_monitors.DiskSpaceMonitor.<init>(DiskSpaceMonitor.java:49)
	at …
Caused: java.lang.IllegalArgumentException: Failed to instantiate class hudson.node_monitors.DiskSpaceMonitor from {"freeSpaceThreshold":"1GiB"}
	at org.kohsuke.stapler.RequestImpl$TypePair.convertJSON(RequestImpl.java:771)
	at org.kohsuke.stapler.RequestImpl.bindJSON(RequestImpl.java:551)
	at org.kohsuke.stapler.RequestImpl.bindJSON(RequestImpl.java:546)
	at hudson.model.Descriptor.bindJSON(Descriptor.java:623)
	at hudson.model.Descriptor.newInstance(Descriptor.java:593)
Caused: java.lang.LinkageError: Failed to instantiate class hudson.node_monitors.DiskSpaceMonitor from {"freeSpaceThreshold":"1GiB"}
	at hudson.model.Descriptor.newInstance(Descriptor.java:596)
	at hudson.util.DescribableList.rebuild(DescribableList.java:183)
	at hudson.model.ComputerSet.doConfigSubmit(ComputerSet.java:355)
	at …

this.freeSpaceWarningThreshold = "2GiB";
}

public Object readResolve() {
if (freeSpaceWarningThreshold == null) {
if (freeSpaceThreshold != null) {
Pattern p = Pattern.compile("(\\d+)(.*)");
Matcher m = p.matcher(freeSpaceThreshold);
if (m.matches()) {
String digits = m.group(1);
String unit = m.group(2);
try {
int wt = Integer.parseInt(digits) * 2;
freeSpaceWarningThreshold = wt + unit;
} catch (NumberFormatException nfe) {
// unreachable
freeSpaceWarningThreshold = "2GiB";
}
}
} else {
freeSpaceWarningThreshold = "2GiB";
}
}
return this;
}

@DataBoundSetter
public void setFreeSpaceWarningThreshold(String freeSpaceWarningThreshold) {
this.freeSpaceWarningThreshold = freeSpaceWarningThreshold;
}

public String getFreeSpaceWarningThreshold() {
return freeSpaceWarningThreshold;
}

public long getThresholdBytes() {
if (freeSpaceThreshold == null)
return DEFAULT_THRESHOLD; // backward compatibility with the data format that didn't have 'freeSpaceThreshold'
try {
return DiskSpace.parse(freeSpaceThreshold).size;
} catch (ParseException e) {
return DEFAULT_THRESHOLD;
}
}

@Override
public Object data(Computer c) {
DiskSpace size = markNodeOfflineIfDiskspaceIsTooLow(c);
protected long getThresholdBytes(Computer c) {
return getThresholdBytes();
}

// mark online (again), if free space is over threshold
if (size != null && size.size > getThresholdBytes() && c.isOffline() && c.getOfflineCause() instanceof DiskSpace)
if (this.getClass().equals(((DiskSpace) c.getOfflineCause()).getTrigger()))
if (getDescriptor().markOnline(c)) {
LOGGER.info(Messages.DiskSpaceMonitor_MarkedOnline(c.getDisplayName()));
}
return size;
protected long getWarningThresholdBytes() {
if (freeSpaceWarningThreshold == null)
return DEFAULT_THRESHOLD * 2;
try {
return DiskSpace.parse(freeSpaceWarningThreshold).size;
} catch (ParseException e) {
return DEFAULT_THRESHOLD * 2;
}
}

/**
* Marks the given node as offline if free disk space is below the configured threshold.
* @param c the node
* @return the free space
* @since 1.521
*/
@Restricted(NoExternalUse.class)
public DiskSpace markNodeOfflineIfDiskspaceIsTooLow(Computer c) {
protected long getWarningThresholdBytes(Computer c) {
return getWarningThresholdBytes();
}

@Override
public Object data(Computer c) {
DiskSpace size = (DiskSpace) super.data(c);
if (size != null && size.size < getThresholdBytes()) {
size.setTriggered(this.getClass(), true);
if (getDescriptor().markOffline(c, size)) {
LOGGER.warning(Messages.DiskSpaceMonitor_MarkedOffline(c.getDisplayName()));
}
}
((DiskSpaceMonitorDescriptor) getDescriptor()).markNodeOfflineOrOnline(c, size, this);
return size;
}

Expand Down
32 changes: 32 additions & 0 deletions core/src/main/java/hudson/node_monitors/DiskSpaceMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,38 @@ public DiskSpaceMonitor(String freeSpaceThreshold) throws ParseException {

public DiskSpaceMonitor() {}

@Override
public long getThresholdBytes(Computer c) {
Node node = c.getNode();
if (node != null) {
DiskSpaceMonitorNodeProperty nodeProperty = node.getNodeProperty(DiskSpaceMonitorNodeProperty.class);
if (nodeProperty != null) {
try {
return DiskSpace.parse(nodeProperty.getFreeDiskSpaceThreshold()).size;
} catch (ParseException e) {
return getThresholdBytes();
}
}
}
return getThresholdBytes();
}

@Override
protected long getWarningThresholdBytes(Computer c) {
Node node = c.getNode();
if (node != null) {
DiskSpaceMonitorNodeProperty nodeProperty = node.getNodeProperty(DiskSpaceMonitorNodeProperty.class);
if (nodeProperty != null) {
try {
return DiskSpace.parse(nodeProperty.getFreeDiskSpaceWarningThreshold()).size;
} catch (ParseException e) {
return getWarningThresholdBytes();
}
}
}
return getWarningThresholdBytes();
}

public DiskSpace getFreeSpace(Computer c) {
return DESCRIPTOR.get(c);
}
Expand Down
Loading