Skip to content

Commit

Permalink
Merge pull request #8658 from MarkEWaite/stable-2.426-backports
Browse files Browse the repository at this point in the history
Backporting for 2.426.1
  • Loading branch information
MarkEWaite authored Oct 31, 2023
2 parents b114fa6 + b560797 commit 9f3db79
Show file tree
Hide file tree
Showing 15 changed files with 204 additions and 66 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/hudson/model/Slave.java
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ protected Slave(@NonNull String name, String nodeDescription, String remoteFS, i
this.numExecutors = numExecutors;
this.mode = mode;
this.remoteFS = Util.fixNull(remoteFS).trim();
this.labelAtomSet = Collections.unmodifiableSet(Label.parse(labelString));
_setLabelString(labelString);
this.launcher = launcher;
this.retentionStrategy = retentionStrategy;
getAssignedLabels(); // compute labels now
Expand Down
24 changes: 24 additions & 0 deletions core/src/main/java/jenkins/agents/CloudSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
Expand Down Expand Up @@ -248,6 +253,25 @@ public synchronized void doDoCreate(StaplerRequest req, StaplerResponse rsp,
rsp.sendRedirect2(".");
}

@POST
public void doReorder(StaplerRequest req, StaplerResponse rsp) throws IOException {
Jenkins.get().checkPermission(Jenkins.ADMINISTER);
var names = req.getParameterValues("name");
if (names == null) {
throw new Failure("No cloud names given");
}
var namesList = Arrays.asList(names);
var clouds = new ArrayList<>(Jenkins.get().clouds);
Collections.sort(clouds, Comparator.comparingInt(c -> getIndexOf(namesList, c)));
Jenkins.get().clouds.replaceBy(clouds);
rsp.sendRedirect2(".");
}

private static int getIndexOf(List<String> namesList, Cloud cloud) {
var i = namesList.indexOf(cloud.name);
return i == -1 ? Integer.MAX_VALUE : i;
}

@Extension
public static class DescriptorImpl extends Descriptor<CloudSet> implements StaplerProxy {

Expand Down
65 changes: 65 additions & 0 deletions core/src/main/java/jenkins/telemetry/impl/Uptime.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* The MIT License
*
* Copyright (c) 2023, 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.telemetry.impl;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import java.time.LocalDate;
import jenkins.telemetry.Telemetry;
import net.sf.json.JSONObject;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

/**
* Records approximations of when Jenkins was started and the current time, to allow for computation of uptime.
*/
@Extension
@Restricted(NoExternalUse.class)
public class Uptime extends Telemetry {
private static final long START = System.nanoTime();

@NonNull
@Override
public String getDisplayName() {
return "Uptime";
}

@NonNull
@Override
public LocalDate getStart() {
return LocalDate.of(2023, 10, 20);
}

@NonNull
@Override
public LocalDate getEnd() {
return LocalDate.of(2024, 1, 20);
}

@Override
public JSONObject createContent() {
return new JSONObject().element("start", START).element("now", System.nanoTime()).element("components", buildComponentInformation());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ THE SOFTWARE.
xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form"
xmlns:i="jelly:fmt" xmlns:p="/lib/hudson/project">
<j:set var="escapeEntryTitleAndDescription" value="false"/>
<f:entry>
<f:checkbox title="${h.escape(it.name)}" description="${it.formattedDescription}" name="value" checked="${it.value}" readonly="true" />
<f:entry description="${it.formattedDescription}">
<f:checkbox title="${h.escape(it.name)}" name="value" checked="${it.value}" readonly="true" />
</f:entry>
</j:jelly>
76 changes: 46 additions & 30 deletions core/src/main/resources/jenkins/agents/CloudSet/index.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,14 @@ THE SOFTWARE.
Entrance to the configuration page
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout">
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout" xmlns:f="/lib/form">
<l:layout title="${it.displayName}" type="one-column">
<l:isAdmin>
<l:header>
<script src="${resURL}/jsbundles/pages/cloud-set.js" type="text/javascript" />
<link rel="stylesheet" href="${resURL}/jsbundles/pages/cloud-set.css" type="text/css" />
</l:header>
</l:isAdmin>
<l:main-panel>
<j:choose>
<j:when test="${it.cloudAvailable and it.hasClouds()}">
Expand All @@ -39,36 +45,46 @@ THE SOFTWARE.
</a>
</l:isAdmin>
</l:app-bar>
<table id="clouds" class="jenkins-table sortable">
<thead>
<tr>
<th class="jenkins-table__cell--tight"/>
<th initialSortDir="down">${%Name}</th>
<th class="jenkins-table__cell--tight"/>
</tr>
</thead>
<tbody>
<j:forEach var="cloud" items="${it.clouds}">
<tr id="cloud_${cloud.name}">
<td data="${cloud.icon}" class="jenkins-table__cell--tight jenkins-table__icon">
<div class="jenkins-table__cell__button-wrapper">
<l:icon src="${cloud.iconClassName}" tooltip="${cloud.iconAltText}"/>
</div>
</td>
<td>
<a href="${it.getCloudUrl(request,app,cloud)}" class="jenkins-table__link model-link inside">${cloud.name}</a>
</td>
<td class="jenkins-table__cell--tight">
<div class="jenkins-table__cell__button-wrapper">
<a href="${it.getCloudUrl(request,app,cloud)}configure" class="jenkins-table__button">
<l:icon src="symbol-settings"/>
</a>
</div>
</td>
<p class="description">${%description}</p>
<f:form method="post" name="config" action="reorder">
<table id="clouds" class="jenkins-table">
<thead>
<tr>
<th tooltip="${%Drag and drop cells to reorder}">${%Order}</th>
<th>${%Name}</th>
<th/>
</tr>
</j:forEach>
</tbody>
</table>
</thead>
<tbody class="with-drag-drop">
<j:forEach var="cloud" items="${it.clouds}">
<tr class="repeated-chunk" id="cloud_${cloud.name}">
<td data="${cloud.icon}" class="jenkins-table__cell--tight jenkins-table__icon">
<div class="jenkins-table__cell__button-wrapper dd-handle">
<l:icon src="${cloud.iconClassName}" tooltip="${cloud.iconAltText}"/>
</div>
</td>
<td>
<input type="hidden" name="name" value="${cloud.name}" />
<a href="${it.getCloudUrl(request,app,cloud)}" class="jenkins-table__link model-link inside">${cloud.name}</a>
</td>
<td class="jenkins-table__cell--tight">
<div class="jenkins-table__cell__button-wrapper">
<a href="${it.getCloudUrl(request,app,cloud)}configure" class="jenkins-table__button">
<l:icon src="symbol-settings"/>
</a>
</div>
</td>
</tr>
</j:forEach>
</tbody>
</table>
<l:isAdmin>
<f:bottomButtonBar>
<f:submit id="saveButton" value="${%Save}" clazz="jenkins-hidden" />
</f:bottomButtonBar>
</l:isAdmin>
</f:form>
<st:adjunct includes="lib.form.confirm" />
</j:when>
<j:otherwise>
<div class="empty-state-block">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ noCloudAvailable=There are no clouds currently setup, create one or install a pl
noCloudPlugin=There are no cloud implementations for dynamically allocated agents installed.
newCloud=New cloud
installCloudPlugin=Install a plugin
description=During node provisioning, clouds are tried in the order they appear in this table.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core">
This trial collects two timestamps:
<ul>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/System.html#nanoTime()"><code>System#nanoTime</code></a> when the trial was initialized (corresponds roughly to when Jenkins was started)</li>
<li><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/System.html#nanoTime()"><code>System#nanoTime</code></a> when the trial data was collected (i.e., when it was prepared just before submission)</li>
</ul>

Additionally this trial collects the list of installed plugins, their version, and the version of Jenkins.
This data will help understand unexpected submission frequency of other trials' data.
</j:jelly>
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ THE SOFTWARE.
<changelog.url>https://www.jenkins.io/changelog</changelog.url>

<!-- Bundled Remoting version -->
<remoting.version>3148.v532a_7e715ee3</remoting.version>
<remoting.version>3160.vd76b_9ddd10cc</remoting.version>
<!-- Minimum Remoting version, which is tested for API compatibility -->
<remoting.minimum.supported.version>4.13</remoting.minimum.supported.version>

Expand All @@ -98,7 +98,7 @@ THE SOFTWARE.
<bridge-method-injector.version>1.29</bridge-method-injector.version>
<spotless.check.skip>false</spotless.check.skip>
<!-- Make sure to keep the jetty-maven-plugin version in war/pom.xml in sync with the Jetty release in Winstone: -->
<winstone.version>6.13</winstone.version>
<winstone.version>6.14</winstone.version>
</properties>

<!--
Expand Down
2 changes: 1 addition & 1 deletion war/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ THE SOFTWARE.
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>10.0.16</version>
<version>10.0.17</version>
<configuration>
<!--
Reload webapp when you hit ENTER. (See JETTY-282 for more)
Expand Down
37 changes: 18 additions & 19 deletions war/src/main/js/components/dropdowns/hetero-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,23 @@ function generateHandles() {
});
}

function convertInputsToButtons(e) {
let oldInputs = e.querySelectorAll("INPUT.hetero-list-add");
oldInputs.forEach((oldbtn) => {
let btn = document.createElement("button");
btn.setAttribute("type", "button");
btn.classList.add("hetero-list-add", "jenkins-button");
btn.innerText = oldbtn.getAttribute("value");
if (oldbtn.hasAttribute("suffix")) {
btn.setAttribute("suffix", oldbtn.getAttribute("suffix"));
}
let chevron = createElementFromHtml(Symbols.CHEVRON_DOWN);
btn.appendChild(chevron);
oldbtn.parentNode.appendChild(btn);
oldbtn.remove();
});
}

function generateButtons() {
behaviorShim.specify(
"DIV.hetero-list-container",
Expand All @@ -31,26 +48,8 @@ function generateButtons() {
return;
}

convertInputsToButtons(e);
let btn = Array.from(e.querySelectorAll("BUTTON.hetero-list-add")).pop();
if (!btn) {
let oldbtn = Array.from(
e.querySelectorAll("INPUT.hetero-list-add"),
).pop();
if (!oldbtn) {
return;
}
btn = document.createElement("button");
btn.setAttribute("type", "button");
btn.classList.add("hetero-list-add", "jenkins-button");
btn.innerText = oldbtn.getAttribute("value");
if (oldbtn.hasAttribute("suffix")) {
btn.setAttribute("suffix", oldbtn.getAttribute("suffix"));
}
let chevron = createElementFromHtml(Symbols.CHEVRON_DOWN);
btn.appendChild(chevron);
oldbtn.parentNode.appendChild(btn);
oldbtn.remove();
}

let prototypes = e.lastElementChild;
while (!prototypes.classList.contains("prototypes")) {
Expand Down
9 changes: 9 additions & 0 deletions war/src/main/js/pages/cloud-set/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { registerSortableTableDragDrop } from "@/sortable-drag-drop";

document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll("tbody").forEach((table) =>
registerSortableTableDragDrop(table, function () {
document.getElementById("saveButton").classList.remove("jenkins-hidden");
}),
);
});
3 changes: 3 additions & 0 deletions war/src/main/js/pages/cloud-set/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.dd-handle {
cursor: move;
}
16 changes: 16 additions & 0 deletions war/src/main/js/sortable-drag-drop.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@ function registerSortableDragDrop(e) {
});
}

export function registerSortableTableDragDrop(e, onChangeFunction) {
if (!e || !e.classList.contains("with-drag-drop")) {
return false;
}

Sortable.create(e, {
handle: ".dd-handle",
items: "tr",
onChange: function (event) {
if (onChangeFunction) {
onChangeFunction(event);
}
},
});
}

/*
* Expose the function to register drag & drop components to the window objects
* so that other widgets can use it (repeatable, hetero-list)
Expand Down
12 changes: 1 addition & 11 deletions war/src/main/webapp/scripts/hudson-behavior.js
Original file line number Diff line number Diff line change
Expand Up @@ -639,17 +639,7 @@ function updateValidationArea(validationArea, content) {
// Only change content if different, causes an unnecessary animation otherwise
if (validationArea.innerHTML !== content) {
validationArea.innerHTML = content;
validationArea.style.height =
validationArea.children[0].offsetHeight + "px";

// Only include the notice in the validation-error-area, move all other elements out
if (validationArea.children.length > 1) {
Array.from(validationArea.children)
.slice(1)
.forEach((element) => {
validationArea.after(element);
});
}
validationArea.style.height = "auto";

Behaviour.applySubtree(validationArea);
// For errors with additional details, apply the subtree to the expandable details pane
Expand Down
4 changes: 4 additions & 0 deletions war/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ module.exports = (env, argv) => ({
),
],
app: [path.join(__dirname, "src/main/js/app.js")],
"pages/cloud-set": [
path.join(__dirname, "src/main/js/pages/cloud-set/index.js"),
path.join(__dirname, "src/main/js/pages/cloud-set/index.scss"),
],
"pages/manage-jenkins": [
path.join(__dirname, "src/main/js/pages/manage-jenkins"),
],
Expand Down

0 comments on commit 9f3db79

Please sign in to comment.