Skip to content

Commit

Permalink
Display appropriate GUI that accurately displays offline by design (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Vlatombe authored Oct 23, 2024
1 parent 2da45fc commit d9fbb65
Show file tree
Hide file tree
Showing 46 changed files with 323 additions and 57 deletions.
44 changes: 12 additions & 32 deletions core/src/main/java/hudson/model/Computer.java
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ public AnnotatedLargeText<Computer> getLogText() {
* null if the system was put offline without given a cause.
*/
@Exported
@Override
public OfflineCause getOfflineCause() {
var node = getNode();
if (node != null) {
Expand All @@ -374,19 +375,7 @@ public boolean hasOfflineCause() {
@Exported
@Override
public String getOfflineCauseReason() {
var offlineCause = getOfflineCause();
if (offlineCause == null) {
return "";
}
// fetch the localized string for "Disconnected By"
String gsub_base = hudson.slaves.Messages.SlaveComputer_DisconnectedBy("", "");
// regex to remove commented reason base string
String gsub1 = "^" + gsub_base + "[\\w\\W]* \\: ";
// regex to remove non-commented reason base string
String gsub2 = "^" + gsub_base + "[\\w\\W]*";

String newString = offlineCause.toString().replaceAll(gsub1, "");
return newString.replaceAll(gsub2, "");
return IComputer.super.getOfflineCauseReason();
}

/**
Expand Down Expand Up @@ -677,6 +666,14 @@ public boolean isTemporarilyOffline() {
return node != null && node.isTemporarilyOffline();
}

/**
* Allows a caller to define an {@link OfflineCause} for a computer that has never been online.
* @since TODO
*/
public void setOfflineCause(OfflineCause cause) {
this.offlineCause = cause;
}

/**
* @deprecated as of 1.320.
* Use {@link #setTemporaryOfflineCause(OfflineCause)}
Expand Down Expand Up @@ -736,37 +733,20 @@ public String getTemporaryOfflineCauseReason() {
@Exported
@Override
public String getIcon() {
// The machine was taken offline by someone
if (isTemporarilyOffline() && getOfflineCause() instanceof OfflineCause.UserCause) return "symbol-computer-disconnected";
// The computer is not accepting tasks, e.g. because the availability demands it being offline.
if (!isAcceptingTasks()) {
return "symbol-computer-not-accepting";
}
// The computer is not connected or it is temporarily offline due to a node monitor
if (isOffline()) return "symbol-computer-offline";
return "symbol-computer";
return IComputer.super.getIcon();
}

/**
* {@inheritDoc}
*
* <p>
* It is both the recommended and default implementation to serve different icons based on {@link #isOffline}.
* @see #getIcon()
*/
@Exported
@Override
public String getIconClassName() {
return IComputer.super.getIconClassName();
}

public String getIconAltText() {
// The machine was taken offline by someone
if (isTemporarilyOffline() && getOfflineCause() instanceof OfflineCause.UserCause) return "[temporarily offline by user]";
// There is a "technical" reason the computer will not accept new builds
if (isOffline() || !isAcceptingTasks()) return "[offline]";
return "[online]";
}

@Exported
@Override public @NonNull String getDisplayName() {
return nodeName;
Expand Down
57 changes: 45 additions & 12 deletions core/src/main/java/hudson/slaves/OfflineCause.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import hudson.model.User;
import java.io.ObjectStreamException;
import java.util.Collections;
import java.util.Date;
import jenkins.agents.IOfflineCause;
import jenkins.model.Jenkins;
import org.jvnet.localizer.Localizable;
import org.kohsuke.accmod.Restricted;
Expand All @@ -51,28 +51,20 @@
* @since 1.320
*/
@ExportedBean
public abstract class OfflineCause {
public abstract class OfflineCause implements IOfflineCause {
protected final long timestamp = System.currentTimeMillis();

/**
* Timestamp in which the event happened.
* {@inheritDoc}
*
* @since 1.612
*/
@Exported
@Override
public long getTimestamp() {
return timestamp;
}

/**
* Same as {@link #getTimestamp()} but in a different type.
*
* @since 1.612
*/
public final @NonNull Date getTime() {
return new Date(timestamp);
}

/**
* @deprecated Only exists for backward compatibility.
* @see Computer#setTemporarilyOffline(boolean)
Expand Down Expand Up @@ -192,6 +184,24 @@ private Object readResolve() throws ObjectStreamException {
}
return this;
}

@Override
@NonNull
public String getComputerIcon() {
return "symbol-computer-disconnected";
}

@Override
@NonNull
public String getComputerIconAltText() {
return "[temporarily offline by user]";
}

@NonNull
@Override
public String getIcon() {
return "symbol-person";

Check warning on line 203 in core/src/main/java/hudson/slaves/OfflineCause.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 191-203 are not covered by tests
}
}

public static class ByCLI extends UserCause {
Expand All @@ -212,5 +222,28 @@ public static class IdleOfflineCause extends SimpleOfflineCause {
public IdleOfflineCause() {
super(hudson.slaves.Messages._RetentionStrategy_Demand_OfflineIdle());
}

@Override
@NonNull
public String getComputerIcon() {
return "symbol-computer-paused";
}

@Override
@NonNull
public String getComputerIconAltText() {
return "[will connect automatically whenever needed]";
}

@Override
@NonNull
public String getIcon() {
return "symbol-pause";
}

@Override
public String getStatusClass() {
return "info";

Check warning on line 246 in core/src/main/java/hudson/slaves/OfflineCause.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 235-246 are not covered by tests
}
}
}
2 changes: 2 additions & 0 deletions core/src/main/java/hudson/slaves/RetentionStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ public long check(final SlaveComputer c) {
logger.log(Level.INFO, "Launching computer {0} as it has been in demand for {1}",
new Object[]{c.getName(), Util.getTimeSpanString(demandMilliseconds)});
c.connect(false);
} else if (c.getOfflineCause() == null) {
c.setOfflineCause(new OfflineCause.IdleOfflineCause());

Check warning on line 276 in core/src/main/java/hudson/slaves/RetentionStrategy.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 275-276 are not covered by tests
}
} else if (c.isIdle()) {
final long idleMilliseconds = System.currentTimeMillis() - c.getIdleStartMilliseconds();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ public void run() {
c.disconnect(OfflineCause.create(Messages._SimpleScheduledRetentionStrategy_FinishedUpTime()));
}
}
} else {
c.setOfflineCause(new ScheduledOfflineCause());
}
return 0;
}
Expand All @@ -252,6 +254,24 @@ private synchronized boolean isOnlineScheduled() {
return (lastStart < now && lastStop > now) || (nextStart < now && nextStop > now);
}

public static class ScheduledOfflineCause extends OfflineCause.SimpleOfflineCause {
public ScheduledOfflineCause() {
super(Messages._SimpleScheduledRetentionStrategy_ScheduledOfflineCause_displayName());
}

@NonNull
@Override
public String getComputerIcon() {
return "symbol-computer-not-accepting";
}

@NonNull
@Override
public String getIcon() {
return "symbol-trigger";

Check warning on line 271 in core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 246-271 are not covered by tests
}
}

@Extension @Symbol("schedule")
public static class DescriptorImpl extends Descriptor<RetentionStrategy<?>> {
@NonNull
Expand Down
108 changes: 108 additions & 0 deletions core/src/main/java/jenkins/agents/IOfflineCause.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* The MIT License
*
* Copyright 2024 CloudBees, Inc.
*
* 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 jenkins.agents;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Util;
import java.util.Date;
import java.util.Objects;
import jenkins.model.IComputer;

/**
* Represents a cause that puts a {@linkplain IComputer#isOffline() computer offline}.
* @since TODO
*/
public interface IOfflineCause {
/**
* @return The icon to use for the computer that has this offline cause. It will be displayed in the build executor status widget, as well as in nodes list screen.
*/
@NonNull
default String getComputerIcon() {
return "symbol-computer-offline";
}

/**
* @return The alt text for the icon returned by {@link #getComputerIcon()}.
*/
@NonNull
default String getComputerIconAltText() {
return "[offline]";

Check warning on line 50 in core/src/main/java/jenkins/agents/IOfflineCause.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 50 is not covered by tests
}

/**
* @return The icon to render this offline cause. It will be displayed in the build executor status widget.
*/
@NonNull
default String getIcon() {
return "symbol-error";
}

/**
* @return The reason why this offline cause exists.
* <p>
* For implementers: this can use HTML formatting, so make sure to only include trusted content.
*/
@NonNull
default String getReason() {
// fetch the localized string for "Disconnected By"
String gsub_base = hudson.slaves.Messages.SlaveComputer_DisconnectedBy("", "");
// regex to remove commented reason base string
String gsub1 = "^" + gsub_base + "[\\w\\W]* \\: ";
// regex to remove non-commented reason base string
String gsub2 = "^" + gsub_base + "[\\w\\W]*";
return Objects.requireNonNull(Util.escape(toString().replaceAll(gsub1, "").replaceAll(gsub2, "")));
}

/**
* @return A short message (one word) that summarizes the offline cause.
*
* <p>
* For implementers: this can use HTML formatting, so make sure to only include trusted content.
*/
@NonNull
default String getMessage() {
return Messages.IOfflineCause_offline();

Check warning on line 85 in core/src/main/java/jenkins/agents/IOfflineCause.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 85 is not covered by tests
}

/**
* @return the CSS class name that should be used to render the status.
*/
@SuppressWarnings("unused") // jelly
default String getStatusClass() {
return "warning";
}

/**
* Timestamp in which the event happened.
*/
long getTimestamp();

/**
* Same as {@link #getTimestamp()} but in a different type.
*/
@NonNull
default Date getTime() {
return new Date(getTimestamp());
}
}
Loading

0 comments on commit d9fbb65

Please sign in to comment.