Skip to content

Commit

Permalink
Merge branch 'master' into rework-layout-jelly-2
Browse files Browse the repository at this point in the history
  • Loading branch information
janfaracik committed Jan 3, 2024
2 parents 1462b1f + 444f2de commit 9b44aa7
Show file tree
Hide file tree
Showing 24 changed files with 680 additions and 293 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/publish-release-artifact.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ jobs:
is-rc: ${{ steps.set-version.outputs.is-rc }}
steps:
- uses: actions/checkout@v4
- name: Set up JDK 11
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: 11
java-version: 17
cache: "maven"
- name: Set version
id: set-version
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This page provides information about contributing code to the Jenkins core codeb
1. Fork the repository on GitHub
2. Clone the forked repository to your machine
3. Install the necessary development tools. In order to develop Jenkins, you need the following:
- Java Development Kit (JDK) 11 or 17.
- Java Development Kit (JDK) 11, 17 or 21.
In the Jenkins project we usually use [Eclipse Temurin](https://adoptium.net/) or [OpenJDK](https://openjdk.java.net/), but you can use other JDKs as well.
- Apache Maven 3.8.1 or above. You can [download Maven here](https://maven.apache.org/download.cgi).
In the Jenkins project we usually use the most recent Maven release.
Expand Down
8 changes: 4 additions & 4 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
Expand All @@ -39,7 +39,7 @@ THE SOFTWARE.

<properties>
<asm.version>9.6</asm.version>
<slf4jVersion>2.0.9</slf4jVersion>
<slf4jVersion>2.0.10</slf4jVersion>
<stapler.version>1822.v120278426e1c</stapler.version>
<groovy.version>2.4.21</groovy.version>
</properties>
Expand Down Expand Up @@ -196,9 +196,9 @@ THE SOFTWARE.
<version>${groovy.version}</version>
</dependency>
<dependency>
<groupId>org.connectbot.jbcrypt</groupId>
<groupId>org.connectbot</groupId>
<artifactId>jbcrypt</artifactId>
<version>1.0.0</version>
<version>1.0.2</version>
</dependency>
<dependency>
<!-- Groovy shell uses this, but it doesn't declare the dependency -->
Expand Down
2 changes: 1 addition & 1 deletion cli/pom.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
Expand Down
4 changes: 2 additions & 2 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
Expand Down Expand Up @@ -282,7 +282,7 @@ THE SOFTWARE.
<artifactId>groovy-all</artifactId>
</dependency>
<dependency>
<groupId>org.connectbot.jbcrypt</groupId>
<groupId>org.connectbot</groupId>
<artifactId>jbcrypt</artifactId>
</dependency>
<dependency>
Expand Down
42 changes: 40 additions & 2 deletions core/src/main/java/hudson/FilePath.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

import static hudson.Util.fileToPath;
import static hudson.Util.fixEmpty;
import static hudson.Util.fixEmptyAndTrim;

import com.google.common.annotations.VisibleForTesting;
import com.jcraft.jzlib.GZIPInputStream;
Expand Down Expand Up @@ -962,7 +963,7 @@ public Void invoke(File dir, VirtualChannel channel) throws IOException {
* </ul>
*
* @param archive
* The resource that represents the tgz/zip file. This URL must support the {@code Last-Modified} header.
* The resource that represents the tgz/zip file. This URL must support the {@code Last-Modified} header or the {@code ETag} header.
* (For example, you could use {@link ClassLoader#getResource}.)
* @param listener
* If non-null, a message will be printed to this listener once this method decides to
Expand All @@ -984,12 +985,18 @@ private boolean installIfNecessaryFrom(@NonNull URL archive, @NonNull TaskListen
try {
FilePath timestamp = this.child(".timestamp");
long lastModified = timestamp.lastModified();
// https://httpwg.org/specs/rfc9110.html#field.etag is the ETag specification
// Read previously stored ETag if timestamp is available
String etag = timestamp.exists() ? fixEmptyAndTrim(timestamp.readToString()) : null;
URLConnection con;
try {
con = ProxyConfiguration.open(archive);
if (lastModified != 0) {
con.setIfModifiedSince(lastModified);
}
if (etag != null) {
con.setRequestProperty("If-None-Match", etag);
}
con.connect();
} catch (IOException x) {
if (this.exists()) {
Expand All @@ -1016,7 +1023,7 @@ private boolean installIfNecessaryFrom(@NonNull URL archive, @NonNull TaskListen
return false;
}
}
if (lastModified != 0) {
if (lastModified != 0 || etag != null) {
if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
return false;
} else if (responseCode != HttpURLConnection.HTTP_OK) {
Expand All @@ -1027,8 +1034,12 @@ private boolean installIfNecessaryFrom(@NonNull URL archive, @NonNull TaskListen
}

long sourceTimestamp = con.getLastModified();
String resultEtag = fixEmptyAndTrim(con.getHeaderField("ETag"));

if (this.exists()) {
if (equalETags(etag, resultEtag)) {
return false; // already up to date
}
if (lastModified != 0 && sourceTimestamp == lastModified)
return false; // already up to date
this.deleteContents();
Expand All @@ -1042,6 +1053,10 @@ private boolean installIfNecessaryFrom(@NonNull URL archive, @NonNull TaskListen
// First try to download from the agent machine.
try {
act(new Unpack(archive));
if (resultEtag != null && !equalETags(etag, resultEtag)) {
/* Store the ETag value in the timestamp file for later use */
timestamp.write(resultEtag, "UTF-8");
}
timestamp.touch(sourceTimestamp);
return true;
} catch (IOException x) {
Expand All @@ -1061,13 +1076,36 @@ private boolean installIfNecessaryFrom(@NonNull URL archive, @NonNull TaskListen
throw new IOException(String.format("Failed to unpack %s (%d bytes read of total %d)",
archive, cis.getByteCount(), con.getContentLength()), e);
}
if (resultEtag != null && !equalETags(etag, resultEtag)) {
/* Store the ETag value in the timestamp file for later use */
timestamp.write(resultEtag, "UTF-8");
}
timestamp.touch(sourceTimestamp);
return true;
} catch (IOException e) {
throw new IOException("Failed to install " + archive + " to " + remote, e);
}
}

/* Return true if etag1 equals etag2 as defined by the etag specification
https://httpwg.org/specs/rfc9110.html#field.etag
*/
private boolean equalETags(String etag1, String etag2) {
if (etag1 == null || etag2 == null) {
return false;
}
if (etag1.equals(etag2)) {
return true;
}
/* Weak tags are identified by leading characters "W/" as a marker */
/* Weak tag marker must not be considered in tag comparison.
This implements the weak comparison in the specification at
https://httpwg.org/specs/rfc9110.html#field.etag */
String opaqueTag1 = etag1.startsWith("W/") ? etag1.substring(2) : etag1;
String opaqueTag2 = etag2.startsWith("W/") ? etag2.substring(2) : etag2;
return opaqueTag1.equals(opaqueTag2);
}

// this reads from arbitrary URL
private static final class Unpack extends MasterToSlaveFileCallable<Void> {
private final URL archive;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ public boolean isManualLaunchAllowed(final SlaveComputer c) {
return isOnlineScheduled();
}

@Override
public boolean isAcceptingTasks(SlaveComputer c) {
return isOnlineScheduled();
}

@Override
@GuardedBy("hudson.model.Queue.lock")
public synchronized long check(final SlaveComputer c) {
Expand All @@ -191,53 +196,51 @@ public void run() {
LOGGER.log(INFO,
"Enabling new jobs for computer {0} as it has started its scheduled uptime",
new Object[]{c.getName()});
c.setAcceptingTasks(true);
}
} catch (InterruptedException | ExecutionException e) {
}
}
});
}
} else if (!shouldBeOnline && c.isOnline()) {
if (keepUpWhenActive) {
if (!c.isIdle() && c.isAcceptingTasks()) {
c.setAcceptingTasks(false);
LOGGER.log(INFO,
"Disabling new jobs for computer {0} as it has finished its scheduled uptime",
new Object[]{c.getName()});
return 1;
} else if (c.isIdle() && c.isAcceptingTasks()) {
Queue.withLock(new Runnable() {
@Override
public void run() {
if (c.isIdle()) {
LOGGER.log(INFO, "Disconnecting computer {0} as it has finished its scheduled uptime",
new Object[]{c.getName()});
c.disconnect(OfflineCause
.create(Messages._SimpleScheduledRetentionStrategy_FinishedUpTime()));
} else {
c.setAcceptingTasks(false);
if (c.isLaunchSupported()) {
if (keepUpWhenActive) {
if (!c.isIdle() && c.isAcceptingTasks()) {
LOGGER.log(INFO,
"Disabling new jobs for computer {0} as it has finished its scheduled uptime",
new Object[]{c.getName()});
return 1;
} else if (c.isIdle() && c.isAcceptingTasks()) {
Queue.withLock(new Runnable() {
@Override
public void run() {
if (c.isIdle()) {
LOGGER.log(INFO, "Disconnecting computer {0} as it has finished its scheduled uptime",
new Object[]{c.getName()});
c.disconnect(OfflineCause
.create(Messages._SimpleScheduledRetentionStrategy_FinishedUpTime()));
}
}
}
});
} else if (c.isIdle() && !c.isAcceptingTasks()) {
Queue.withLock(new Runnable() {
@Override
public void run() {
if (c.isIdle()) {
LOGGER.log(INFO, "Disconnecting computer {0} as it has finished all jobs running when "
+ "it completed its scheduled uptime", new Object[]{c.getName()});
c.disconnect(OfflineCause
.create(Messages._SimpleScheduledRetentionStrategy_FinishedUpTime()));
});
} else if (c.isIdle() && !c.isAcceptingTasks()) {
Queue.withLock(new Runnable() {
@Override
public void run() {
if (c.isIdle()) {
LOGGER.log(INFO, "Disconnecting computer {0} as it has finished all jobs running when "
+ "it completed its scheduled uptime", new Object[]{c.getName()});
c.disconnect(OfflineCause
.create(Messages._SimpleScheduledRetentionStrategy_FinishedUpTime()));
}
}
}
});
});
}
} else {
// no need to get the queue lock as the user has selected the break builds option!
LOGGER.log(INFO, "Disconnecting computer {0} as it has finished its scheduled uptime",
new Object[]{c.getName()});
c.disconnect(OfflineCause.create(Messages._SimpleScheduledRetentionStrategy_FinishedUpTime()));
}
} else {
// no need to get the queue lock as the user has selected the break builds option!
LOGGER.log(INFO, "Disconnecting computer {0} as it has finished its scheduled uptime",
new Object[]{c.getName()});
c.disconnect(OfflineCause.create(Messages._SimpleScheduledRetentionStrategy_FinishedUpTime()));
}
}
return 1;
Expand Down
17 changes: 17 additions & 0 deletions core/src/main/java/hudson/util/AtomicFileWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import hudson.Functions;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.lang.ref.Cleaner;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.AtomicMoveNotSupportedException;
Expand Down Expand Up @@ -62,6 +64,9 @@ public class AtomicFileWriter extends Writer {
private static /* final */ boolean DISABLE_FORCED_FLUSH = SystemProperties.getBoolean(
AtomicFileWriter.class.getName() + ".DISABLE_FORCED_FLUSH");

private static /* final */ boolean REQUIRES_DIR_FSYNC = SystemProperties.getBoolean(
AtomicFileWriter.class.getName() + ".REQUIRES_DIR_FSYNC", !Functions.isWindows());

Check warning on line 68 in core/src/main/java/hudson/util/AtomicFileWriter.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 68 is only partially covered, one branch is missing

static {
if (DISABLE_FORCED_FLUSH) {
LOGGER.log(Level.WARNING, "DISABLE_FORCED_FLUSH flag used, this could result in dataloss if failures happen in your storage subsystem.");
Expand Down Expand Up @@ -234,6 +239,18 @@ public void commit() throws IOException {
throw replaceFailed;
}
}

/*
* From fsync(2) on Linux:
*
* Calling fsync() does not necessarily ensure that the entry in the directory containing the file has also
* reached disk. For that an explicit fsync() on a file descriptor for the directory is also needed.
*/
if (!DISABLE_FORCED_FLUSH && REQUIRES_DIR_FSYNC) {

Check warning on line 249 in core/src/main/java/hudson/util/AtomicFileWriter.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 249 is only partially covered, 2 branches are missing
try (FileChannel parentChannel = FileChannel.open(destPath.getParent())) {
parentChannel.force(true);
}
}
}

private static final class CleanupChecker implements Runnable {
Expand Down
5 changes: 3 additions & 2 deletions core/src/main/resources/hudson/model/ComputerSet/index.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ THE SOFTWARE.

<j:set var="monitors" value="${it._monitors}"/>
<j:set var="tableWidth" value="${3}"/>

<table id="computers" class="jenkins-table sortable">
<t:setIconSize/>
<table id="computers" class="jenkins-table ${iconSize == '16x16' ? 'jenkins-table--small' : iconSize == '24x24' ? 'jenkins-table--medium' : ''} sortable">
<thead>
<tr>
<th class="jenkins-table__cell--tight">S</th>
Expand Down Expand Up @@ -123,6 +123,7 @@ THE SOFTWARE.
</tr>
</tbody>
</table>
<t:iconSize/>
</l:main-panel>
</l:layout>
</j:jelly>
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ THE SOFTWARE.
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler">
<st:include page="/hudson/security/SecurityRealm/loginLink.jelly" />
<j:if test="${it.allowsSignup()}">
|
<a href="${rootURL}/signup"><b>${%sign up}</b></a>
<a href="${rootURL}/signup">${%sign up}</a>
</j:if>
</j:jelly>
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ THE SOFTWARE.

<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler">
<a href="${rootURL}/${app.securityRealm.loginUrl}?from=${app.securityRealm.from}"><b>${%login}</b></a>
<a href="${rootURL}/${app.securityRealm.loginUrl}?from=${app.securityRealm.from}">${%login}</a>
</j:jelly>
Loading

0 comments on commit 9b44aa7

Please sign in to comment.