From 0a7b656af6ac5c70a408abb082f95ecd51ee8bd0 Mon Sep 17 00:00:00 2001 From: christ66 Date: Fri, 16 Sep 2016 21:41:12 -0700 Subject: [PATCH 0001/2185] [JENKINS-34855] Make atomic file write more atomic by using JDK 7 apis. --- .../java/hudson/util/AtomicFileWriter.java | 45 +++++++++++++------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/hudson/util/AtomicFileWriter.java b/core/src/main/java/hudson/util/AtomicFileWriter.java index f49d4b9ff5..e789fb9faa 100644 --- a/core/src/main/java/hudson/util/AtomicFileWriter.java +++ b/core/src/main/java/hudson/util/AtomicFileWriter.java @@ -32,6 +32,12 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileAttribute; /** * Buffered {@link FileWriter} that supports atomic operations. @@ -45,8 +51,8 @@ import java.nio.charset.Charset; public class AtomicFileWriter extends Writer { private final Writer core; - private final File tmpFile; - private final File destFile; + private final Path tmpFile; + private final Path destFile; /** * Writes with UTF-8 encoding. @@ -60,17 +66,17 @@ public class AtomicFileWriter extends Writer { * File encoding to write. If null, platform default encoding is chosen. */ public AtomicFileWriter(File f, String encoding) throws IOException { - File dir = f.getParentFile(); + Path dir = f.toPath().getParent(); try { - dir.mkdirs(); - tmpFile = File.createTempFile("atomic",null, dir); + Files.createDirectories(dir); + tmpFile = Files.createTempFile(dir, "atomic", null, (FileAttribute) null); } catch (IOException e) { throw new IOException("Failed to create a temporary file in "+ dir,e); } - destFile = f; + destFile = f.toPath(); if (encoding==null) encoding = Charset.defaultCharset().name(); - core = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(tmpFile),encoding)); + core = Files.newBufferedWriter(tmpFile, Charset.forName(encoding), StandardOpenOption.SYNC); } @Override @@ -103,34 +109,47 @@ public class AtomicFileWriter extends Writer { */ public void abort() throws IOException { close(); - tmpFile.delete(); + Files.deleteIfExists(tmpFile); } public void commit() throws IOException { close(); - if (destFile.exists()) { + if (Files.exists(destFile)) { try { - Util.deleteFile(destFile); + Files.delete(tmpFile); // First try with NIO. + Util.deleteFile(destFile.toFile()); // Then try with the util method. } catch (IOException x) { - tmpFile.delete(); + Files.delete(tmpFile); throw x; } } - tmpFile.renameTo(destFile); + Files.move(tmpFile, destFile, null); } @Override protected void finalize() throws Throwable { // one way or the other, temporary file should be deleted. close(); - tmpFile.delete(); + Files.deleteIfExists(tmpFile); } /** * Until the data is committed, this file captures * the written content. + * + * @deprecated Use getTemporaryPath() for JDK 7+ */ + @Deprecated public File getTemporaryFile() { + return tmpFile.toFile(); + } + + /** + * Until the data is committed, this file captures + * the written content. + * + */ + public Path getTemporaryPath() { return tmpFile; } } -- GitLab From f9f10adf259851e1d90e5fe4833b871bdefa840f Mon Sep 17 00:00:00 2001 From: christ66 Date: Sat, 17 Sep 2016 00:11:30 -0700 Subject: [PATCH 0002/2185] Perform ATOMIC_MOVE when copying the file. If we are unable to perform the ATOMIC_MOVE operation then we fallback to an operation which is supported by all OSes. Create constructor for charsets. Delete tempdir instead of destdir. --- .../java/hudson/util/AtomicFileWriter.java | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/hudson/util/AtomicFileWriter.java b/core/src/main/java/hudson/util/AtomicFileWriter.java index e789fb9faa..31cb18901c 100644 --- a/core/src/main/java/hudson/util/AtomicFileWriter.java +++ b/core/src/main/java/hudson/util/AtomicFileWriter.java @@ -32,9 +32,12 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.Charset; +import java.nio.file.AtomicMoveNotSupportedException; +import java.nio.file.CopyOption; import java.nio.file.Files; import java.nio.file.OpenOption; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileAttribute; @@ -64,8 +67,18 @@ public class AtomicFileWriter extends Writer { /** * @param encoding * File encoding to write. If null, platform default encoding is chosen. + * + * @deprecated Use AtomicFileWriter */ public AtomicFileWriter(File f, String encoding) throws IOException { + this(f, Charset.forName(encoding)); + } + + /** + * @param charset + * File charset to write. If null, platform default encoding is chosen. + */ + public AtomicFileWriter(File f, Charset charset) throws IOException { Path dir = f.toPath().getParent(); try { Files.createDirectories(dir); @@ -74,9 +87,9 @@ public class AtomicFileWriter extends Writer { throw new IOException("Failed to create a temporary file in "+ dir,e); } destFile = f.toPath(); - if (encoding==null) - encoding = Charset.defaultCharset().name(); - core = Files.newBufferedWriter(tmpFile, Charset.forName(encoding), StandardOpenOption.SYNC); + if (charset==null) + charset = Charset.defaultCharset(); + core = Files.newBufferedWriter(tmpFile, charset, StandardOpenOption.SYNC); } @Override @@ -116,14 +129,20 @@ public class AtomicFileWriter extends Writer { close(); if (Files.exists(destFile)) { try { - Files.delete(tmpFile); // First try with NIO. - Util.deleteFile(destFile.toFile()); // Then try with the util method. + Util.deleteFile(tmpFile.toFile()); } catch (IOException x) { Files.delete(tmpFile); throw x; } } - Files.move(tmpFile, destFile, null); + try { + // Try to make an atomic move. + Files.move(tmpFile, destFile, StandardCopyOption.ATOMIC_MOVE); + } catch (IOException e) { + // If it falls here that means that Atomic move is not supported by the OS. + // In this case we need to fall-back to a copy option which is supported by all OSes. + Files.move(tmpFile, destFile, StandardCopyOption.REPLACE_EXISTING); + } } @Override -- GitLab From c960c75c86025b778a8a17397d6770f92a6fdf28 Mon Sep 17 00:00:00 2001 From: christ66 Date: Sat, 17 Sep 2016 00:27:29 -0700 Subject: [PATCH 0003/2185] Finish javadoc deprecated statement. --- core/src/main/java/hudson/util/AtomicFileWriter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/util/AtomicFileWriter.java b/core/src/main/java/hudson/util/AtomicFileWriter.java index 31cb18901c..0e0c6f4cd2 100644 --- a/core/src/main/java/hudson/util/AtomicFileWriter.java +++ b/core/src/main/java/hudson/util/AtomicFileWriter.java @@ -68,7 +68,7 @@ public class AtomicFileWriter extends Writer { * @param encoding * File encoding to write. If null, platform default encoding is chosen. * - * @deprecated Use AtomicFileWriter + * @deprecated Use {@link #AtomicFileWriter(File, Charset)} */ public AtomicFileWriter(File f, String encoding) throws IOException { this(f, Charset.forName(encoding)); -- GitLab From 88a6f76c1ba6272ccb169703643b42cebeabe193 Mon Sep 17 00:00:00 2001 From: christ66 Date: Sat, 17 Sep 2016 10:09:22 -0700 Subject: [PATCH 0004/2185] Commit should not perform a delete as it is already performing a move. Add suffix to create temp file. --- core/src/main/java/hudson/util/AtomicFileWriter.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/core/src/main/java/hudson/util/AtomicFileWriter.java b/core/src/main/java/hudson/util/AtomicFileWriter.java index 0e0c6f4cd2..f6d58c5295 100644 --- a/core/src/main/java/hudson/util/AtomicFileWriter.java +++ b/core/src/main/java/hudson/util/AtomicFileWriter.java @@ -82,7 +82,7 @@ public class AtomicFileWriter extends Writer { Path dir = f.toPath().getParent(); try { Files.createDirectories(dir); - tmpFile = Files.createTempFile(dir, "atomic", null, (FileAttribute) null); + tmpFile = Files.createTempFile(dir, "atomic", "tmp"); } catch (IOException e) { throw new IOException("Failed to create a temporary file in "+ dir,e); } @@ -127,14 +127,6 @@ public class AtomicFileWriter extends Writer { public void commit() throws IOException { close(); - if (Files.exists(destFile)) { - try { - Util.deleteFile(tmpFile.toFile()); - } catch (IOException x) { - Files.delete(tmpFile); - throw x; - } - } try { // Try to make an atomic move. Files.move(tmpFile, destFile, StandardCopyOption.ATOMIC_MOVE); -- GitLab From 8434afc25036dc9105ac67dcb486181716fca7a8 Mon Sep 17 00:00:00 2001 From: christ66 Date: Sat, 17 Sep 2016 11:56:22 -0700 Subject: [PATCH 0005/2185] Add unit test for atomic file writer. --- .../hudson/util/AtomicFileWriterTest.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 core/src/test/java/hudson/util/AtomicFileWriterTest.java diff --git a/core/src/test/java/hudson/util/AtomicFileWriterTest.java b/core/src/test/java/hudson/util/AtomicFileWriterTest.java new file mode 100644 index 0000000000..ba8e22ae2a --- /dev/null +++ b/core/src/test/java/hudson/util/AtomicFileWriterTest.java @@ -0,0 +1,75 @@ +package hudson.util; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; + +import static org.junit.Assert.*; + +public class AtomicFileWriterTest { + File af; + AtomicFileWriter afw; + String expectedContent = "hello world"; + + + @Before + public void setUp() throws IOException { + af = File.createTempFile("AtomicFileWriter", ".tmp"); + afw = new AtomicFileWriter(af, Charset.defaultCharset()); + } + + @After + public void tearDown() throws IOException { + Files.deleteIfExists(af.toPath()); + Files.deleteIfExists(afw.getTemporaryPath()); + } + + @Test + public void createFile() throws Exception { + // Verify the file we created exists + assertTrue(Files.exists(afw.getTemporaryPath())); + } + + @Test + public void writeToAtomicFile() throws Exception { + // Given + afw.write(expectedContent, 0, expectedContent.length()); + + // When + afw.flush(); + + // Then + assertTrue("File writer did not properly flush to temporary file", + Files.size(afw.getTemporaryPath()) == expectedContent.length()); + } + + @Test + public void commitToFile() throws Exception { + // Given + afw.write(expectedContent, 0, expectedContent.length()); + + // When + afw.commit(); + + // Then + System.err.println(Files.size(af.toPath())); + assertTrue(Files.size(af.toPath()) == expectedContent.length()); + } + + @Test + public void abortDeletesTmpFile() throws Exception { + // Given + afw.write(expectedContent, 0, expectedContent.length()); + + // When + afw.abort(); + + // Then + assertTrue(Files.notExists(afw.getTemporaryPath())); + } +} \ No newline at end of file -- GitLab From f8c9c04ce44b1aff89bd2af09c7a0b3ec1b18ed5 Mon Sep 17 00:00:00 2001 From: christ66 Date: Sun, 18 Sep 2016 09:35:50 -0700 Subject: [PATCH 0006/2185] Fix javadocs. --- core/src/main/java/hudson/util/AtomicFileWriter.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/hudson/util/AtomicFileWriter.java b/core/src/main/java/hudson/util/AtomicFileWriter.java index f6d58c5295..89d5a0b14c 100644 --- a/core/src/main/java/hudson/util/AtomicFileWriter.java +++ b/core/src/main/java/hudson/util/AtomicFileWriter.java @@ -65,8 +65,7 @@ public class AtomicFileWriter extends Writer { } /** - * @param encoding - * File encoding to write. If null, platform default encoding is chosen. + * @param encoding File encoding to write. If null, platform default encoding is chosen. * * @deprecated Use {@link #AtomicFileWriter(File, Charset)} */ @@ -75,8 +74,7 @@ public class AtomicFileWriter extends Writer { } /** - * @param charset - * File charset to write. If null, platform default encoding is chosen. + * @param charset File charset to write. If null, platform default encoding is chosen. */ public AtomicFileWriter(File f, Charset charset) throws IOException { Path dir = f.toPath().getParent(); -- GitLab From 8d64ff51a95b53f1a56922906e13aef8f279d512 Mon Sep 17 00:00:00 2001 From: christ66 Date: Sun, 18 Sep 2016 09:42:37 -0700 Subject: [PATCH 0007/2185] Do not create the directory if it already exists. --- core/src/main/java/hudson/util/AtomicFileWriter.java | 4 +++- core/src/test/java/hudson/util/AtomicFileWriterTest.java | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/hudson/util/AtomicFileWriter.java b/core/src/main/java/hudson/util/AtomicFileWriter.java index 89d5a0b14c..eeaac35e09 100644 --- a/core/src/main/java/hudson/util/AtomicFileWriter.java +++ b/core/src/main/java/hudson/util/AtomicFileWriter.java @@ -79,7 +79,9 @@ public class AtomicFileWriter extends Writer { public AtomicFileWriter(File f, Charset charset) throws IOException { Path dir = f.toPath().getParent(); try { - Files.createDirectories(dir); + if (Files.notExists(dir)) { + Files.createDirectories(dir); + } tmpFile = Files.createTempFile(dir, "atomic", "tmp"); } catch (IOException e) { throw new IOException("Failed to create a temporary file in "+ dir,e); diff --git a/core/src/test/java/hudson/util/AtomicFileWriterTest.java b/core/src/test/java/hudson/util/AtomicFileWriterTest.java index ba8e22ae2a..6eb881d5ab 100644 --- a/core/src/test/java/hudson/util/AtomicFileWriterTest.java +++ b/core/src/test/java/hudson/util/AtomicFileWriterTest.java @@ -57,7 +57,6 @@ public class AtomicFileWriterTest { afw.commit(); // Then - System.err.println(Files.size(af.toPath())); assertTrue(Files.size(af.toPath()) == expectedContent.length()); } -- GitLab From 5d40c78e5f811e35f5aa18c4d611579335955f14 Mon Sep 17 00:00:00 2001 From: christ66 Date: Thu, 22 Sep 2016 14:17:16 -0400 Subject: [PATCH 0008/2185] Move destFile initiation to top. --- core/src/main/java/hudson/util/AtomicFileWriter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/hudson/util/AtomicFileWriter.java b/core/src/main/java/hudson/util/AtomicFileWriter.java index eeaac35e09..6076d43904 100644 --- a/core/src/main/java/hudson/util/AtomicFileWriter.java +++ b/core/src/main/java/hudson/util/AtomicFileWriter.java @@ -77,7 +77,8 @@ public class AtomicFileWriter extends Writer { * @param charset File charset to write. If null, platform default encoding is chosen. */ public AtomicFileWriter(File f, Charset charset) throws IOException { - Path dir = f.toPath().getParent(); + destFile = f.toPath(); + Path dir = destFile.getParent(); try { if (Files.notExists(dir)) { Files.createDirectories(dir); @@ -86,7 +87,6 @@ public class AtomicFileWriter extends Writer { } catch (IOException e) { throw new IOException("Failed to create a temporary file in "+ dir,e); } - destFile = f.toPath(); if (charset==null) charset = Charset.defaultCharset(); core = Files.newBufferedWriter(tmpFile, charset, StandardOpenOption.SYNC); -- GitLab From e98d8002d8ce1646d00be54d82d2489cb549d354 Mon Sep 17 00:00:00 2001 From: christ66 Date: Thu, 22 Sep 2016 14:19:43 -0400 Subject: [PATCH 0009/2185] Only catch AtomicMoveNotSupportedException. --- core/src/main/java/hudson/util/AtomicFileWriter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/util/AtomicFileWriter.java b/core/src/main/java/hudson/util/AtomicFileWriter.java index 6076d43904..5ce2cff032 100644 --- a/core/src/main/java/hudson/util/AtomicFileWriter.java +++ b/core/src/main/java/hudson/util/AtomicFileWriter.java @@ -130,7 +130,7 @@ public class AtomicFileWriter extends Writer { try { // Try to make an atomic move. Files.move(tmpFile, destFile, StandardCopyOption.ATOMIC_MOVE); - } catch (IOException e) { + } catch (AtomicMoveNotSupportedException e) { // If it falls here that means that Atomic move is not supported by the OS. // In this case we need to fall-back to a copy option which is supported by all OSes. Files.move(tmpFile, destFile, StandardCopyOption.REPLACE_EXISTING); -- GitLab From 2df07337ed4ca894c85f2bd90da9f1e1290f8439 Mon Sep 17 00:00:00 2001 From: christ66 Date: Thu, 22 Sep 2016 14:30:39 -0400 Subject: [PATCH 0010/2185] Use temporary file, and assertEquals in tests. --- .../java/hudson/util/AtomicFileWriterTest.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/core/src/test/java/hudson/util/AtomicFileWriterTest.java b/core/src/test/java/hudson/util/AtomicFileWriterTest.java index 6eb881d5ab..49fd588a53 100644 --- a/core/src/test/java/hudson/util/AtomicFileWriterTest.java +++ b/core/src/test/java/hudson/util/AtomicFileWriterTest.java @@ -2,7 +2,9 @@ package hudson.util; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import java.io.File; import java.io.IOException; @@ -15,20 +17,15 @@ public class AtomicFileWriterTest { File af; AtomicFileWriter afw; String expectedContent = "hello world"; + @Rule public TemporaryFolder tmp = new TemporaryFolder(); @Before public void setUp() throws IOException { - af = File.createTempFile("AtomicFileWriter", ".tmp"); + af = tmp.newFile(); afw = new AtomicFileWriter(af, Charset.defaultCharset()); } - @After - public void tearDown() throws IOException { - Files.deleteIfExists(af.toPath()); - Files.deleteIfExists(afw.getTemporaryPath()); - } - @Test public void createFile() throws Exception { // Verify the file we created exists @@ -44,8 +41,8 @@ public class AtomicFileWriterTest { afw.flush(); // Then - assertTrue("File writer did not properly flush to temporary file", - Files.size(afw.getTemporaryPath()) == expectedContent.length()); + assertEquals("File writer did not properly flush to temporary file", + expectedContent.length(), Files.size(afw.getTemporaryPath())); } @Test @@ -57,7 +54,7 @@ public class AtomicFileWriterTest { afw.commit(); // Then - assertTrue(Files.size(af.toPath()) == expectedContent.length()); + assertEquals(expectedContent.length(), Files.size(af.toPath())); } @Test -- GitLab From a1fae1cc1fd2670269e8ee94e89e3c6c0e05b580 Mon Sep 17 00:00:00 2001 From: David Rutqvist Date: Sun, 9 Oct 2016 15:29:03 +0200 Subject: [PATCH 0011/2185] Made autocomplete for new job case-insensitive --- .../main/java/hudson/model/AutoCompletionCandidates.java | 8 +++++--- core/src/main/java/hudson/model/ComputerSet.java | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/hudson/model/AutoCompletionCandidates.java b/core/src/main/java/hudson/model/AutoCompletionCandidates.java index 0373b552bb..719b050a76 100644 --- a/core/src/main/java/hudson/model/AutoCompletionCandidates.java +++ b/core/src/main/java/hudson/model/AutoCompletionCandidates.java @@ -118,16 +118,18 @@ public class AutoCompletionCandidates implements HttpResponse { @Override public void onItem(Item i) { String n = contextualNameOf(i); - if ((n.startsWith(value) || value.startsWith(n)) + String lowerCaseN = n.toLowerCase(); + String lowerCaseValue = value.toLowerCase(); + if ((lowerCaseN.startsWith(lowerCaseValue) || lowerCaseValue.startsWith(lowerCaseN)) // 'foobar' is a valid candidate if the current value is 'foo'. // Also, we need to visit 'foo' if the current value is 'foo/bar' - && (value.length()>n.length() || !n.substring(value.length()).contains("/")) + && (lowerCaseValue.length()>lowerCaseN.length() || !lowerCaseN.substring(lowerCaseValue.length()).contains("/")) // but 'foobar/zot' isn't if the current value is 'foo' // we'll first show 'foobar' and then wait for the user to type '/' to show the rest && i.hasPermission(Item.READ) // and read permission required ) { - if (type.isInstance(i) && n.startsWith(value)) + if (type.isInstance(i) && lowerCaseN.startsWith(lowerCaseValue)) candidates.add(n); // recurse diff --git a/core/src/main/java/hudson/model/ComputerSet.java b/core/src/main/java/hudson/model/ComputerSet.java index 89ed31f289..6e7e0af9df 100644 --- a/core/src/main/java/hudson/model/ComputerSet.java +++ b/core/src/main/java/hudson/model/ComputerSet.java @@ -388,7 +388,7 @@ public final class ComputerSet extends AbstractModelObject implements Describabl final AutoCompletionCandidates r = new AutoCompletionCandidates(); for (Node n : Jenkins.getInstance().getNodes()) { - if (n.getNodeName().startsWith(value)) + if (n.getNodeName().toLowerCase().contains(value.toLowerCase())) r.add(n.getNodeName()); } -- GitLab From c8f5dcf21feae1bc6b1b9f6364480c90bf73a08e Mon Sep 17 00:00:00 2001 From: David Rutqvist Date: Tue, 11 Oct 2016 16:03:10 +0200 Subject: [PATCH 0012/2185] Revert "Made autocomplete for new job case-insensitive" This reverts commit a1fae1cc1fd2670269e8ee94e89e3c6c0e05b580. --- .../main/java/hudson/model/AutoCompletionCandidates.java | 8 +++----- core/src/main/java/hudson/model/ComputerSet.java | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/hudson/model/AutoCompletionCandidates.java b/core/src/main/java/hudson/model/AutoCompletionCandidates.java index 719b050a76..0373b552bb 100644 --- a/core/src/main/java/hudson/model/AutoCompletionCandidates.java +++ b/core/src/main/java/hudson/model/AutoCompletionCandidates.java @@ -118,18 +118,16 @@ public class AutoCompletionCandidates implements HttpResponse { @Override public void onItem(Item i) { String n = contextualNameOf(i); - String lowerCaseN = n.toLowerCase(); - String lowerCaseValue = value.toLowerCase(); - if ((lowerCaseN.startsWith(lowerCaseValue) || lowerCaseValue.startsWith(lowerCaseN)) + if ((n.startsWith(value) || value.startsWith(n)) // 'foobar' is a valid candidate if the current value is 'foo'. // Also, we need to visit 'foo' if the current value is 'foo/bar' - && (lowerCaseValue.length()>lowerCaseN.length() || !lowerCaseN.substring(lowerCaseValue.length()).contains("/")) + && (value.length()>n.length() || !n.substring(value.length()).contains("/")) // but 'foobar/zot' isn't if the current value is 'foo' // we'll first show 'foobar' and then wait for the user to type '/' to show the rest && i.hasPermission(Item.READ) // and read permission required ) { - if (type.isInstance(i) && lowerCaseN.startsWith(lowerCaseValue)) + if (type.isInstance(i) && n.startsWith(value)) candidates.add(n); // recurse diff --git a/core/src/main/java/hudson/model/ComputerSet.java b/core/src/main/java/hudson/model/ComputerSet.java index 6e7e0af9df..89ed31f289 100644 --- a/core/src/main/java/hudson/model/ComputerSet.java +++ b/core/src/main/java/hudson/model/ComputerSet.java @@ -388,7 +388,7 @@ public final class ComputerSet extends AbstractModelObject implements Describabl final AutoCompletionCandidates r = new AutoCompletionCandidates(); for (Node n : Jenkins.getInstance().getNodes()) { - if (n.getNodeName().toLowerCase().contains(value.toLowerCase())) + if (n.getNodeName().startsWith(value)) r.add(n.getNodeName()); } -- GitLab From 6b933bd383824e94f672b8e3bd66ed36f111a7a0 Mon Sep 17 00:00:00 2001 From: David Rutqvist Date: Tue, 11 Oct 2016 16:38:23 +0200 Subject: [PATCH 0013/2185] Redid fix so it uses the user setting used by global search instead of defaulting to case-insensitive --- .../hudson/model/AutoCompletionCandidates.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/hudson/model/AutoCompletionCandidates.java b/core/src/main/java/hudson/model/AutoCompletionCandidates.java index 0373b552bb..57f79eae99 100644 --- a/core/src/main/java/hudson/model/AutoCompletionCandidates.java +++ b/core/src/main/java/hudson/model/AutoCompletionCandidates.java @@ -25,6 +25,7 @@ package hudson.model; import hudson.search.Search; +import hudson.search.UserSearchProperty; import jenkins.model.Jenkins; import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.StaplerRequest; @@ -118,16 +119,26 @@ public class AutoCompletionCandidates implements HttpResponse { @Override public void onItem(Item i) { String n = contextualNameOf(i); - if ((n.startsWith(value) || value.startsWith(n)) + boolean caseInsensitive = UserSearchProperty.isCaseInsensitive(); + + String hay = n; + String needle = value; + + if(caseInsensitive) { + hay = hay.toLowerCase(); + needle = needle.toLowerCase(); + } + + if ((hay.startsWith(needle) || needle.startsWith(hay)) // 'foobar' is a valid candidate if the current value is 'foo'. // Also, we need to visit 'foo' if the current value is 'foo/bar' - && (value.length()>n.length() || !n.substring(value.length()).contains("/")) + && (needle.length()>hay.length() || !hay.substring(needle.length()).contains("/")) // but 'foobar/zot' isn't if the current value is 'foo' // we'll first show 'foobar' and then wait for the user to type '/' to show the rest && i.hasPermission(Item.READ) // and read permission required ) { - if (type.isInstance(i) && n.startsWith(value)) + if (type.isInstance(i) && hay.startsWith(needle)) candidates.add(n); // recurse -- GitLab From 9fee6cc3f0d2240b8e23b447318fde36b5e0eb8e Mon Sep 17 00:00:00 2001 From: David Rutqvist Date: Tue, 11 Oct 2016 16:54:29 +0200 Subject: [PATCH 0014/2185] [FIXED JENKINS-38812] Commented on the implementation. Uses the same setting as used globally --- .../main/java/hudson/model/AutoCompletionCandidates.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/model/AutoCompletionCandidates.java b/core/src/main/java/hudson/model/AutoCompletionCandidates.java index 57f79eae99..f410633441 100644 --- a/core/src/main/java/hudson/model/AutoCompletionCandidates.java +++ b/core/src/main/java/hudson/model/AutoCompletionCandidates.java @@ -119,7 +119,11 @@ public class AutoCompletionCandidates implements HttpResponse { @Override public void onItem(Item i) { String n = contextualNameOf(i); - boolean caseInsensitive = UserSearchProperty.isCaseInsensitive(); + + //Check user's setting on whether to do case sensitive comparison, configured in user -> configure + //This is the same setting that is used by the global search field, should be consistent throughout + //the whole application. + boolean caseInsensitive = UserSearchProperty.isCaseInsensitive(); String hay = n; String needle = value; -- GitLab From bd844821413de7a0d72f45756b52ce45b78da16b Mon Sep 17 00:00:00 2001 From: Andrew Bayer Date: Mon, 30 Jan 2017 16:47:58 -0800 Subject: [PATCH 0015/2185] [WIP JENKINS-24141] First work on abstracting out changelogs Need to add tests, but first need to determine if this actually makes sense as I've implemented it. --- .../main/java/hudson/model/AbstractBuild.java | 13 ++- .../java/hudson/model/AbstractProject.java | 60 ---------- core/src/main/java/hudson/model/Job.java | 83 +++++++++++++- core/src/main/java/hudson/model/View.java | 107 ++++++++++-------- .../src/main/java/jenkins/scm/RunWithSCM.java | 47 ++++++++ 5 files changed, 199 insertions(+), 111 deletions(-) create mode 100644 core/src/main/java/jenkins/scm/RunWithSCM.java diff --git a/core/src/main/java/hudson/model/AbstractBuild.java b/core/src/main/java/hudson/model/AbstractBuild.java index a096635dfc..6a2b75ae30 100644 --- a/core/src/main/java/hudson/model/AbstractBuild.java +++ b/core/src/main/java/hudson/model/AbstractBuild.java @@ -30,6 +30,7 @@ import hudson.EnvVars; import hudson.FilePath; import hudson.Functions; import hudson.Launcher; +import jenkins.scm.RunWithSCM; import jenkins.util.SystemProperties; import hudson.console.ModelHyperlinkNote; import hudson.model.Fingerprint.BuildPtr; @@ -102,7 +103,7 @@ import org.kohsuke.accmod.restrictions.DoNotUse; * @author Kohsuke Kawaguchi * @see AbstractProject */ -public abstract class AbstractBuild

,R extends AbstractBuild> extends Run implements Queue.Executable, LazyBuildMixIn.LazyLoadingRun { +public abstract class AbstractBuild

,R extends AbstractBuild> extends Run implements Queue.Executable, LazyBuildMixIn.LazyLoadingRun, RunWithSCM { /** * Set if we want the blame information to flow from upstream to downstream build. @@ -331,8 +332,9 @@ public abstract class AbstractBuild

,R extends Abs * @return * can be empty but never null. */ + @Override @Exported - public Set getCulprits() { + @Nonnull public Set getCulprits() { if (culprits==null) { Set r = new HashSet(); R p = getPreviousCompletedBuild(); @@ -386,6 +388,7 @@ public abstract class AbstractBuild

,R extends Abs * * @since 1.191 */ + @Override public boolean hasParticipant(User user) { for (ChangeLogSet.Entry e : getChangeSet()) try{ @@ -863,7 +866,7 @@ public abstract class AbstractBuild

,R extends Abs * @return never null. */ @Exported - public ChangeLogSet getChangeSet() { + @Nonnull public ChangeLogSet getChangeSet() { synchronized (changeSetLock) { if (scm==null) { scm = NullChangeLogParser.INSTANCE; @@ -887,8 +890,8 @@ public abstract class AbstractBuild

,R extends Abs return cs; } - @Restricted(DoNotUse.class) // for project-changes.jelly - public List> getChangeSets() { + @Override + @Nonnull public List> getChangeSets() { ChangeLogSet cs = getChangeSet(); return cs.isEmptySet() ? Collections.>emptyList() : Collections.>singletonList(cs); } diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java index b3d9413635..9291f9b0b7 100644 --- a/core/src/main/java/hudson/model/AbstractProject.java +++ b/core/src/main/java/hudson/model/AbstractProject.java @@ -1971,66 +1971,6 @@ public abstract class AbstractProject

,R extends A } - /** - * RSS feed for changes in this project. - */ - public void doRssChangelog( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { - class FeedItem { - ChangeLogSet.Entry e; - int idx; - - public FeedItem(Entry e, int idx) { - this.e = e; - this.idx = idx; - } - - AbstractBuild getBuild() { - return e.getParent().build; - } - } - - List entries = new ArrayList(); - - for(R r=getLastBuild(); r!=null; r=r.getPreviousBuild()) { - int idx=0; - for( ChangeLogSet.Entry e : r.getChangeSet()) - entries.add(new FeedItem(e,idx++)); - } - - RSS.forwardToRss( - getDisplayName()+' '+getScm().getDescriptor().getDisplayName()+" changes", - getUrl()+"changes", - entries, new FeedAdapter() { - public String getEntryTitle(FeedItem item) { - return "#"+item.getBuild().number+' '+item.e.getMsg()+" ("+item.e.getAuthor()+")"; - } - - public String getEntryUrl(FeedItem item) { - return item.getBuild().getUrl()+"changes#detail"+item.idx; - } - - public String getEntryID(FeedItem item) { - return getEntryUrl(item); - } - - public String getEntryDescription(FeedItem item) { - StringBuilder buf = new StringBuilder(); - for(String path : item.e.getAffectedPaths()) - buf.append(path).append('\n'); - return buf.toString(); - } - - public Calendar getEntryTimestamp(FeedItem item) { - return item.getBuild().getTimestamp(); - } - - public String getEntryAuthor(FeedItem entry) { - return JenkinsLocationConfiguration.get().getAdminAddress(); - } - }, - req, rsp ); - } - /** * {@link AbstractProject} subtypes should implement this base class as a descriptor. * diff --git a/core/src/main/java/hudson/model/Job.java b/core/src/main/java/hudson/model/Job.java index 88d39ac15f..d58ca23fb2 100644 --- a/core/src/main/java/hudson/model/Job.java +++ b/core/src/main/java/hudson/model/Job.java @@ -28,6 +28,7 @@ import hudson.BulkChange; import hudson.EnvVars; import hudson.Extension; import hudson.ExtensionPoint; +import hudson.FeedAdapter; import hudson.PermalinkList; import hudson.Util; import hudson.cli.declarative.CLIResolver; @@ -36,6 +37,8 @@ import hudson.model.Fingerprint.Range; import hudson.model.Fingerprint.RangeSet; import hudson.model.PermalinkProjectAction.Permalink; import hudson.model.listeners.ItemListener; +import hudson.scm.ChangeLogSet; +import hudson.scm.SCM; import hudson.search.QuickSilver; import hudson.search.SearchIndex; import hudson.search.SearchIndexBuilder; @@ -83,11 +86,14 @@ import jenkins.model.BuildDiscarder; import jenkins.model.BuildDiscarderProperty; import jenkins.model.DirectlyModifiableTopLevelItemGroup; import jenkins.model.Jenkins; +import jenkins.model.JenkinsLocationConfiguration; import jenkins.model.ModelObjectWithChildren; import jenkins.model.ProjectNamingStrategy; import jenkins.model.RunIdMigrator; import jenkins.model.lazy.LazyBuildMixIn; +import jenkins.scm.RunWithSCM; import jenkins.security.HexStringConfidentialKey; +import jenkins.triggers.SCMTriggerItem; import jenkins.util.io.OnMaster; import net.sf.json.JSONException; import net.sf.json.JSONObject; @@ -1056,7 +1062,82 @@ public abstract class Job, RunT extends Run getBuild() { + return e.getParent().build; + } + } + + List entries = new ArrayList(); + String scmDisplayName = ""; + if (this instanceof SCMTriggerItem) { + SCMTriggerItem scmItem = (SCMTriggerItem) this; + List scmNames = new ArrayList<>(); + for (SCM s : scmItem.getSCMs()) { + scmNames.add(s.getDescriptor().getDisplayName()); + } + scmDisplayName = " " + Util.join(scmNames, ", "); + + for (RunT r = getLastBuild(); r != null; r = r.getPreviousBuild()) { + int idx = 0; + if (r instanceof RunWithSCM) { + for (ChangeLogSet c : ((RunWithSCM) r).getChangeSets()) { + for (ChangeLogSet.Entry e : c) { + entries.add(new FeedItem(e, idx++)); + } + } + } + } + } + RSS.forwardToRss( + getDisplayName() + scmDisplayName + " changes", + getUrl() + "changes", + entries, new FeedAdapter() { + public String getEntryTitle(FeedItem item) { + return "#" + item.getBuild().number + ' ' + item.e.getMsg() + " (" + item.e.getAuthor() + ")"; + } + + public String getEntryUrl(FeedItem item) { + return item.getBuild().getUrl() + "changes#detail" + item.idx; + } + + public String getEntryID(FeedItem item) { + return getEntryUrl(item); + } + + public String getEntryDescription(FeedItem item) { + StringBuilder buf = new StringBuilder(); + for (String path : item.e.getAffectedPaths()) + buf.append(path).append('\n'); + return buf.toString(); + } + + public Calendar getEntryTimestamp(FeedItem item) { + return item.getBuild().getTimestamp(); + } + + public String getEntryAuthor(FeedItem entry) { + return JenkinsLocationConfiguration.get().getAdminAddress(); + } + }, + req, rsp); + } + + + @Override public ContextMenu doChildrenContextMenu(StaplerRequest request, StaplerResponse response) throws Exception { // not sure what would be really useful here. This needs more thoughts. // for the time being, I'm starting with permalinks diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java index ea3d911700..68dae32760 100644 --- a/core/src/main/java/hudson/model/View.java +++ b/core/src/main/java/hudson/model/View.java @@ -62,6 +62,8 @@ import jenkins.model.ModelObjectWithChildren; import jenkins.model.item_category.Categories; import jenkins.model.item_category.Category; import jenkins.model.item_category.ItemCategory; +import jenkins.scm.RunWithSCM; +import jenkins.triggers.SCMTriggerItem; import jenkins.util.ProgressiveRendering; import jenkins.util.xml.XMLUtils; @@ -615,12 +617,12 @@ public abstract class View extends AbstractModelObject implements AccessControll /** * Which project did this user commit? Can be null. */ - private AbstractProject project; + private Job project; /** @see UserAvatarResolver */ String avatar; - UserInfo(User user, AbstractProject p, Calendar lastChange) { + UserInfo(User user, Job p, Calendar lastChange) { this.user = user; this.project = p; this.lastChange = lastChange; @@ -637,7 +639,7 @@ public abstract class View extends AbstractModelObject implements AccessControll } @Exported - public AbstractProject getProject() { + public Job getProject() { return project; } @@ -720,20 +722,25 @@ public abstract class View extends AbstractModelObject implements AccessControll private Map getUserInfo(Collection items) { Map users = new HashMap(); for (Item item : items) { - for (Job job : item.getAllJobs()) { - if (job instanceof AbstractProject) { - AbstractProject p = (AbstractProject) job; - for (AbstractBuild build : p.getBuilds()) { - for (Entry entry : build.getChangeSet()) { - User user = entry.getAuthor(); - - UserInfo info = users.get(user); - if(info==null) - users.put(user,new UserInfo(user,p,build.getTimestamp())); - else - if(info.getLastChange().before(build.getTimestamp())) { - info.project = p; - info.lastChange = build.getTimestamp(); + for (Job job : item.getAllJobs()) { + if (job instanceof SCMTriggerItem) { + RunList> runs = job.getBuilds(); + for (Run r : runs) { + if (r instanceof RunWithSCM) { + RunWithSCM runWithSCM = (RunWithSCM) r; + + for (ChangeLogSet c: runWithSCM.getChangeSets()) { + for (Entry entry : c) { + User user = entry.getAuthor(); + + UserInfo info = users.get(user); + if (info == null) + users.put(user, new UserInfo(user, job, runWithSCM.getTimestamp())); + else if (info.getLastChange().before(runWithSCM.getTimestamp())) { + info.project = job; + info.lastChange = runWithSCM.getTimestamp(); + } + } } } } @@ -761,13 +768,19 @@ public abstract class View extends AbstractModelObject implements AccessControll public static boolean isApplicable(Collection items) { for (Item item : items) { for (Job job : item.getAllJobs()) { - if (job instanceof AbstractProject) { - AbstractProject p = (AbstractProject) job; - for (AbstractBuild build : p.getBuilds()) { - for (Entry entry : build.getChangeSet()) { - User user = entry.getAuthor(); - if(user!=null) - return true; + if (job instanceof SCMTriggerItem) { + RunList> runs = job.getBuilds(); + + for (Run r : runs) { + if (r instanceof RunWithSCM) { + RunWithSCM runWithSCM = (RunWithSCM) r; + for (ChangeLogSet c : runWithSCM.getChangeSets()) { + for (Entry entry : c) { + User user = entry.getAuthor(); + if (user != null) + return true; + } + } } } } @@ -813,29 +826,33 @@ public abstract class View extends AbstractModelObject implements AccessControll int itemCount = 0; for (Item item : items) { for (Job job : item.getAllJobs()) { - if (job instanceof AbstractProject) { - AbstractProject p = (AbstractProject) job; - RunList> builds = p.getBuilds(); + if (job instanceof SCMTriggerItem) { + RunList> runs = job.getBuilds(); int buildCount = 0; - for (AbstractBuild build : builds) { + for (Run r : runs) { if (canceled()) { return; } - for (ChangeLogSet.Entry entry : build.getChangeSet()) { - User user = entry.getAuthor(); - UserInfo info = users.get(user); - if (info == null) { - UserInfo userInfo = new UserInfo(user, p, build.getTimestamp()); - userInfo.avatar = UserAvatarResolver.resolveOrNull(user, iconSize); - synchronized (this) { - users.put(user, userInfo); - modified.add(user); - } - } else if (info.getLastChange().before(build.getTimestamp())) { - synchronized (this) { - info.project = p; - info.lastChange = build.getTimestamp(); - modified.add(user); + if (r instanceof RunWithSCM) { + RunWithSCM runWithSCM = (RunWithSCM) r; + for (ChangeLogSet c : runWithSCM.getChangeSets()) { + for (ChangeLogSet.Entry entry : c) { + User user = entry.getAuthor(); + UserInfo info = users.get(user); + if (info == null) { + UserInfo userInfo = new UserInfo(user, job, runWithSCM.getTimestamp()); + userInfo.avatar = UserAvatarResolver.resolveOrNull(user, iconSize); + synchronized (this) { + users.put(user, userInfo); + modified.add(user); + } + } else if (info.getLastChange().before(runWithSCM.getTimestamp())) { + synchronized (this) { + info.project = job; + info.lastChange = runWithSCM.getTimestamp(); + modified.add(user); + } + } } } } @@ -843,7 +860,7 @@ public abstract class View extends AbstractModelObject implements AccessControll buildCount++; // TODO this defeats lazy-loading. Should rather do a breadth-first search, as in hudson.plugins.view.dashboard.builds.LatestBuilds // (though currently there is no quick implementation of RunMap.size() ~ idOnDisk.size(), which would be needed for proper progress) - progress((itemCount + 1.0 * buildCount / builds.size()) / (items.size() + 1)); + progress((itemCount + 1.0 * buildCount / runs.size()) / (items.size() + 1)); } } } @@ -884,7 +901,7 @@ public abstract class View extends AbstractModelObject implements AccessControll accumulate("avatar", i.avatar != null ? i.avatar : Stapler.getCurrentRequest().getContextPath() + Functions.getResourcePath() + "/images/" + iconSize + "/user.png"). accumulate("timeSortKey", i.getTimeSortKey()). accumulate("lastChangeTimeString", i.getLastChangeTimeString()); - AbstractProject p = i.getProject(); + Job p = i.getProject(); if (p != null) { entry.accumulate("projectUrl", p.getUrl()).accumulate("projectFullDisplayName", p.getFullDisplayName()); } diff --git a/core/src/main/java/jenkins/scm/RunWithSCM.java b/core/src/main/java/jenkins/scm/RunWithSCM.java new file mode 100644 index 0000000000..2e76c7b8de --- /dev/null +++ b/core/src/main/java/jenkins/scm/RunWithSCM.java @@ -0,0 +1,47 @@ +/* + * The MIT License + * + * Copyright 2014 Jesse Glick. + * + * 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.scm; + +import hudson.model.User; +import hudson.scm.ChangeLogSet; + +import javax.annotation.Nonnull; +import java.util.Calendar; +import java.util.List; +import java.util.Set; + +/** + * @since 2.xxx + */ +public interface RunWithSCM { + + @Nonnull List> getChangeSets(); + + @Nonnull Set getCulprits(); + + @Nonnull Calendar getTimestamp(); + + boolean hasParticipant(User user); +} -- GitLab From 61acefc1dd0166403881487bc953e893b0a1dc19 Mon Sep 17 00:00:00 2001 From: Andrew Bayer Date: Tue, 31 Jan 2017 09:51:12 -0800 Subject: [PATCH 0016/2185] Switch to a mixin approach Also bumping to a distinct SNAPSHOT version for dependency builds. --- cli/pom.xml | 2 +- core/pom.xml | 2 +- .../main/java/hudson/model/AbstractBuild.java | 83 +++------ core/src/main/java/hudson/model/Job.java | 7 +- core/src/main/java/hudson/model/View.java | 84 +++++---- .../src/main/java/jenkins/scm/RunWithSCM.java | 47 ----- .../java/jenkins/scm/RunWithSCMMixIn.java | 161 ++++++++++++++++++ pom.xml | 2 +- test/pom.xml | 2 +- war/pom.xml | 2 +- 10 files changed, 242 insertions(+), 150 deletions(-) delete mode 100644 core/src/main/java/jenkins/scm/RunWithSCM.java create mode 100644 core/src/main/java/jenkins/scm/RunWithSCMMixIn.java diff --git a/cli/pom.xml b/cli/pom.xml index aa26f4a66f..16ecf94e13 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main pom - 2.44-SNAPSHOT + 2.44-JENKINS-24141-SNAPSHOT cli diff --git a/core/pom.xml b/core/pom.xml index d2577beff6..b87b4c6dcf 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.44-SNAPSHOT + 2.44-JENKINS-24141-SNAPSHOT jenkins-core diff --git a/core/src/main/java/hudson/model/AbstractBuild.java b/core/src/main/java/hudson/model/AbstractBuild.java index 6a2b75ae30..80e2bb3a9d 100644 --- a/core/src/main/java/hudson/model/AbstractBuild.java +++ b/core/src/main/java/hudson/model/AbstractBuild.java @@ -30,7 +30,8 @@ import hudson.EnvVars; import hudson.FilePath; import hudson.Functions; import hudson.Launcher; -import jenkins.scm.RunWithSCM; +import jenkins.model.ParameterizedJobMixIn; +import jenkins.scm.RunWithSCMMixIn; import jenkins.util.SystemProperties; import hudson.console.ModelHyperlinkNote; import hudson.model.Fingerprint.BuildPtr; @@ -92,8 +93,6 @@ import static java.util.logging.Level.WARNING; import jenkins.model.lazy.BuildReference; import jenkins.model.lazy.LazyBuildMixIn; -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.DoNotUse; /** * Base implementation of {@link Run}s that build software. @@ -103,7 +102,7 @@ import org.kohsuke.accmod.restrictions.DoNotUse; * @author Kohsuke Kawaguchi * @see AbstractProject */ -public abstract class AbstractBuild

,R extends AbstractBuild> extends Run implements Queue.Executable, LazyBuildMixIn.LazyLoadingRun, RunWithSCM { +public abstract class AbstractBuild

,R extends AbstractBuild> extends Run implements Queue.Executable, LazyBuildMixIn.LazyLoadingRun, RunWithSCMMixIn.RunWithSCM { /** * Set if we want the blame information to flow from upstream to downstream build. @@ -184,6 +183,23 @@ public abstract class AbstractBuild

,R extends Abs return runMixIn; } + @Override + public RunWithSCMMixIn getRunWithSCMMixIn() { + return new RunWithSCMMixIn() { + @SuppressWarnings("unchecked") // untypable + @Override protected R asRun() { + return (R) AbstractBuild.this; + } + + @Override + @Nonnull public List> getChangeSets() { + ChangeLogSet cs = getChangeSet(); + return cs.isEmptySet() ? Collections.>emptyList() : Collections.>singletonList(cs); + } + + }; + } + @Override protected final BuildReference createReference() { return getRunMixIn().createReference(); } @@ -335,52 +351,12 @@ public abstract class AbstractBuild

,R extends Abs @Override @Exported @Nonnull public Set getCulprits() { - if (culprits==null) { - Set r = new HashSet(); - R p = getPreviousCompletedBuild(); - if (p !=null && isBuilding()) { - Result pr = p.getResult(); - if (pr!=null && pr.isWorseThan(Result.SUCCESS)) { - // we are still building, so this is just the current latest information, - // but we seems to be failing so far, so inherit culprits from the previous build. - // isBuilding() check is to avoid recursion when loading data from old Hudson, which doesn't record - // this information - r.addAll(p.getCulprits()); - } - } - for (Entry e : getChangeSet()) - r.add(e.getAuthor()); - - if (upstreamCulprits) { - // If we have dependencies since the last successful build, add their authors to our list - if (getPreviousNotFailedBuild() != null) { - Map depmap = getDependencyChanges(getPreviousSuccessfulBuild()); - for (DependencyChange dep : depmap.values()) { - for (AbstractBuild b : dep.getBuilds()) { - for (Entry entry : b.getChangeSet()) { - r.add(entry.getAuthor()); - } - } - } - } - } - - return r; - } - - return new AbstractSet() { - public Iterator iterator() { - return new AdaptedIterator(culprits.iterator()) { - protected User adapt(String id) { - return User.get(id); - } - }; - } + return getRunWithSCMMixIn().getCulprits(); + } - public int size() { - return culprits.size(); - } - }; + @Override + @CheckForNull public Set getCulpritIds() { + return culprits; } /** @@ -390,14 +366,7 @@ public abstract class AbstractBuild

,R extends Abs */ @Override public boolean hasParticipant(User user) { - for (ChangeLogSet.Entry e : getChangeSet()) - try{ - if (e.getAuthor()==user) - return true; - } catch (RuntimeException re) { - LOGGER.log(Level.INFO, "Failed to determine author of changelog " + e.getCommitId() + "for " + getParent().getDisplayName() + ", " + getDisplayName(), re); - } - return false; + return getRunWithSCMMixIn().hasParticipant(user); } /** diff --git a/core/src/main/java/hudson/model/Job.java b/core/src/main/java/hudson/model/Job.java index d58ca23fb2..09b182b3bf 100644 --- a/core/src/main/java/hudson/model/Job.java +++ b/core/src/main/java/hudson/model/Job.java @@ -91,10 +91,9 @@ import jenkins.model.ModelObjectWithChildren; import jenkins.model.ProjectNamingStrategy; import jenkins.model.RunIdMigrator; import jenkins.model.lazy.LazyBuildMixIn; -import jenkins.scm.RunWithSCM; +import jenkins.scm.RunWithSCMMixIn; import jenkins.security.HexStringConfidentialKey; import jenkins.triggers.SCMTriggerItem; -import jenkins.util.io.OnMaster; import net.sf.json.JSONException; import net.sf.json.JSONObject; import org.apache.commons.io.FileUtils; @@ -1093,8 +1092,8 @@ public abstract class Job, RunT extends Run c : ((RunWithSCM) r).getChangeSets()) { + if (r instanceof RunWithSCMMixIn.RunWithSCM) { + for (ChangeLogSet c : ((RunWithSCMMixIn.RunWithSCM) r).getChangeSets()) { for (ChangeLogSet.Entry e : c) { entries.add(new FeedItem(e, idx++)); } diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java index 68dae32760..49f0648e79 100644 --- a/core/src/main/java/hudson/model/View.java +++ b/core/src/main/java/hudson/model/View.java @@ -59,10 +59,11 @@ import hudson.widgets.Widget; import javax.annotation.Nonnull; import jenkins.model.Jenkins; import jenkins.model.ModelObjectWithChildren; +import jenkins.model.ModelObjectWithContextMenu; import jenkins.model.item_category.Categories; import jenkins.model.item_category.Category; import jenkins.model.item_category.ItemCategory; -import jenkins.scm.RunWithSCM; +import jenkins.scm.RunWithSCMMixIn; import jenkins.triggers.SCMTriggerItem; import jenkins.util.ProgressiveRendering; import jenkins.util.xml.XMLUtils; @@ -116,7 +117,9 @@ import java.util.logging.Level; import java.util.logging.Logger; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; -import static jenkins.model.Jenkins.*; +import static jenkins.model.Jenkins.checkGoodName; +import static jenkins.scm.RunWithSCMMixIn.*; + import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.QueryParameter; @@ -463,7 +466,7 @@ public abstract class View extends AbstractModelObject implements AccessControll private boolean isRelevant(Collection

+ * This list at least always include people who made changes in this build, but + * if the previous build was a failure it also includes the culprit list from there. + * + * @return + * can be empty but never null. + */ + @Exported + @Nonnull public Set getCulprits() { + if (asRun().getCulpritIds() == null) { + Set r = new HashSet(); + RunT p = asRun().getPreviousCompletedBuild(); + if (p != null && asRun().isBuilding()) { + Result pr = p.getResult(); + if (pr != null && pr.isWorseThan(Result.SUCCESS)) { + // we are still building, so this is just the current latest information, + // but we seems to be failing so far, so inherit culprits from the previous build. + // isBuilding() check is to avoid recursion when loading data from old Hudson, which doesn't record + // this information + r.addAll(p.getCulprits()); + } + } + for (ChangeLogSet c : getChangeSets()) { + for (ChangeLogSet.Entry e : c) + r.add(e.getAuthor()); + } + + if (p instanceof AbstractBuild && upstreamCulprits) { + // If we have dependencies since the last successful build, add their authors to our list + if (p.getPreviousNotFailedBuild() != null) { + Map depmap = + ((AbstractBuild) p).getDependencyChanges((AbstractBuild)p.getPreviousSuccessfulBuild()); + for (AbstractBuild.DependencyChange dep : depmap.values()) { + for (AbstractBuild b : dep.getBuilds()) { + for (ChangeLogSet.Entry entry : b.getChangeSet()) { + r.add(entry.getAuthor()); + } + } + } + } + } + + return r; + } + + return new AbstractSet() { + public Iterator iterator() { + return new AdaptedIterator(asRun().getCulpritIds().iterator()) { + protected User adapt(String id) { + return User.get(id); + } + }; + } + + public int size() { + return asRun().getCulpritIds().size(); + } + }; + } + + /** + * Returns true if this user has made a commit to this build. + */ + public boolean hasParticipant(User user) { + for (ChangeLogSet c : getChangeSets()) { + for (ChangeLogSet.Entry e : c) + try { + if (e.getAuthor() == user) + return true; + } catch (RuntimeException re) { + LOGGER.log(Level.INFO, "Failed to determine author of changelog " + e.getCommitId() + "for " + asRun().getParent().getDisplayName() + ", " + asRun().getDisplayName(), re); + } + } + return false; + } + + public interface RunWithSCM & Queue.Task, + RunT extends Run & RunWithSCM & Queue.Executable> { + @Nonnull + List> getChangeSets(); + + @Nonnull + Set getCulprits(); + + @CheckForNull + Set getCulpritIds(); + + RunWithSCMMixIn getRunWithSCMMixIn(); + + boolean hasParticipant(User user); + } + + private static final Logger LOGGER = Logger.getLogger(RunWithSCMMixIn.class.getName()); +} diff --git a/pom.xml b/pom.xml index b230d350e4..5f2291e4ec 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.44-SNAPSHOT + 2.44-JENKINS-24141-SNAPSHOT pom Jenkins main module diff --git a/test/pom.xml b/test/pom.xml index 65f2cc6a7d..6087607a09 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.44-SNAPSHOT + 2.44-JENKINS-24141-SNAPSHOT test diff --git a/war/pom.xml b/war/pom.xml index 282991fc10..d4e169b7b1 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.44-SNAPSHOT + 2.44-JENKINS-24141-SNAPSHOT jenkins-war -- GitLab From d7d4a8244bde232473b79cf5a8ea453392bd3aa2 Mon Sep 17 00:00:00 2001 From: Andrew Bayer Date: Tue, 31 Jan 2017 10:01:34 -0800 Subject: [PATCH 0017/2185] Minor cleanup, fixing AbstractBuild.getChangeSets() --- core/src/main/java/hudson/model/AbstractBuild.java | 5 +---- core/src/main/java/hudson/model/AbstractProject.java | 5 ----- core/src/main/java/hudson/model/View.java | 1 - 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/core/src/main/java/hudson/model/AbstractBuild.java b/core/src/main/java/hudson/model/AbstractBuild.java index 80e2bb3a9d..ce07ab0fda 100644 --- a/core/src/main/java/hudson/model/AbstractBuild.java +++ b/core/src/main/java/hudson/model/AbstractBuild.java @@ -30,7 +30,6 @@ import hudson.EnvVars; import hudson.FilePath; import hudson.Functions; import hudson.Launcher; -import jenkins.model.ParameterizedJobMixIn; import jenkins.scm.RunWithSCMMixIn; import jenkins.util.SystemProperties; import hudson.console.ModelHyperlinkNote; @@ -72,7 +71,6 @@ import java.io.File; import java.io.IOException; import java.io.InterruptedIOException; import java.lang.ref.WeakReference; -import java.util.AbstractSet; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; @@ -861,8 +859,7 @@ public abstract class AbstractBuild

,R extends Abs @Override @Nonnull public List> getChangeSets() { - ChangeLogSet cs = getChangeSet(); - return cs.isEmptySet() ? Collections.>emptyList() : Collections.>singletonList(cs); + return getRunWithSCMMixIn().getChangeSets(); } /** diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java index 9291f9b0b7..5a1dc16370 100644 --- a/core/src/main/java/hudson/model/AbstractProject.java +++ b/core/src/main/java/hudson/model/AbstractProject.java @@ -34,7 +34,6 @@ import hudson.CopyOnWrite; import hudson.EnvVars; import hudson.ExtensionList; import hudson.ExtensionPoint; -import hudson.FeedAdapter; import hudson.FilePath; import hudson.Functions; import hudson.Launcher; @@ -55,8 +54,6 @@ import hudson.model.queue.CauseOfBlockage; import hudson.model.queue.QueueTaskFuture; import hudson.model.queue.SubTask; import hudson.model.queue.SubTaskContributor; -import hudson.scm.ChangeLogSet; -import hudson.scm.ChangeLogSet.Entry; import hudson.scm.NullSCM; import hudson.scm.PollingResult; @@ -87,7 +84,6 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -107,7 +103,6 @@ import javax.annotation.Nonnull; import javax.servlet.ServletException; import jenkins.model.BlockedBecauseOfBuildInProgress; import jenkins.model.Jenkins; -import jenkins.model.JenkinsLocationConfiguration; import jenkins.model.ParameterizedJobMixIn; import jenkins.model.Uptime; import jenkins.model.lazy.LazyBuildMixIn; diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java index 49f0648e79..f8fa9d91a3 100644 --- a/core/src/main/java/hudson/model/View.java +++ b/core/src/main/java/hudson/model/View.java @@ -63,7 +63,6 @@ import jenkins.model.ModelObjectWithContextMenu; import jenkins.model.item_category.Categories; import jenkins.model.item_category.Category; import jenkins.model.item_category.ItemCategory; -import jenkins.scm.RunWithSCMMixIn; import jenkins.triggers.SCMTriggerItem; import jenkins.util.ProgressiveRendering; import jenkins.util.xml.XMLUtils; -- GitLab From 4992b15066ecd87428dcd204102f1158bbcc8b3a Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 1 Feb 2017 04:10:10 -0800 Subject: [PATCH 0018/2185] [maven-release-plugin] prepare release jenkins-2.32.2 --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index 604a6459fc..9d6f977cae 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main pom - 2.32.2-SNAPSHOT + 2.32.2 cli diff --git a/core/pom.xml b/core/pom.xml index 873a43d6d3..f05d116df8 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.32.2-SNAPSHOT + 2.32.2 jenkins-core diff --git a/pom.xml b/pom.xml index 89866c9634..69cbe5841a 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.32.2-SNAPSHOT + 2.32.2 pom Jenkins main module @@ -58,7 +58,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - HEAD + jenkins-2.32.2 diff --git a/test/pom.xml b/test/pom.xml index 045b40c680..83be4abeec 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.32.2-SNAPSHOT + 2.32.2 test diff --git a/war/pom.xml b/war/pom.xml index 0c274318ac..d99e15f03f 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.32.2-SNAPSHOT + 2.32.2 jenkins-war -- GitLab From 3fe1e2c66ed18c283ce4175d69f86bf70c47b01f Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 1 Feb 2017 04:10:10 -0800 Subject: [PATCH 0019/2185] [maven-release-plugin] prepare for next development iteration --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index 9d6f977cae..8b51ad8a42 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main pom - 2.32.2 + 2.32.3-SNAPSHOT cli diff --git a/core/pom.xml b/core/pom.xml index f05d116df8..277d662ff1 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.32.2 + 2.32.3-SNAPSHOT jenkins-core diff --git a/pom.xml b/pom.xml index 69cbe5841a..8bba227ab3 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.32.2 + 2.32.3-SNAPSHOT pom Jenkins main module @@ -58,7 +58,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - jenkins-2.32.2 + HEAD diff --git a/test/pom.xml b/test/pom.xml index 83be4abeec..fc7378f07c 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.32.2 + 2.32.3-SNAPSHOT test diff --git a/war/pom.xml b/war/pom.xml index d99e15f03f..695a25fa30 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.32.2 + 2.32.3-SNAPSHOT jenkins-war -- GitLab From c732d8a44a0c23ef1399a7480b3b455ce6aaf185 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 17 Jan 2017 12:36:54 -0500 Subject: [PATCH 0020/2185] [FIXED JENKINS-37625] Update Winstone to fix an IllegalStateException. (cherry picked from commit 1e5e53a5fbf1e40ba637f1b21214e0fb8a0bee8b) --- war/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/war/pom.xml b/war/pom.xml index 695a25fa30..50f598178b 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -93,7 +93,7 @@ THE SOFTWARE. --> org.jenkins-ci winstone - 3.2 + 3.3 test -- GitLab From 3a2d38378a346e221be70ad8e13d66836ba71876 Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Mon, 2 Jan 2017 19:56:59 +0100 Subject: [PATCH 0021/2185] [FIX JENKINS-38175] Fix various ManagementLink related bugs - In the context menu, the 'post' flag was set iff 'requiresConfirmation' was set, even though they're independent (e.g. Prepare for shutdown requires the former but not the latter) - /manage screen: The icon (t:summary) does not support POST or confirmation links, but was set to not link only if no confirmation was required (i.e. POST links did not POST when the icon was clicked -- now the icon is not clickable as a workaround) - /manage screen: All links requiring confirmation did POST, which masked the fact that the 'Reload from disk' link wasn't set up to require POST (it was only broken in the context menu). Now, confirmation and POST are separate flags, and 'Reload from disk' link now requests POST. (cherry picked from commit c9b878f4889659b889d03e24aa8e5cb6eb763b89) --- core/src/main/java/jenkins/management/ReloadLink.java | 6 +++++- core/src/main/resources/jenkins/model/Jenkins/manage.jelly | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/jenkins/management/ReloadLink.java b/core/src/main/java/jenkins/management/ReloadLink.java index 9159788379..59fabb8ba1 100644 --- a/core/src/main/java/jenkins/management/ReloadLink.java +++ b/core/src/main/java/jenkins/management/ReloadLink.java @@ -56,5 +56,9 @@ public class ReloadLink extends ManagementLink { @Override public boolean getRequiresConfirmation() { return true; } - + + @Override + public boolean getRequiresPOST() { + return true; + } } diff --git a/core/src/main/resources/jenkins/model/Jenkins/manage.jelly b/core/src/main/resources/jenkins/model/Jenkins/manage.jelly index 0769b65bd0..7f9887ad8d 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/manage.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/manage.jelly @@ -32,15 +32,15 @@ THE SOFTWARE. - ${taskTags!=null and attrs.contextMenu!='false' ? taskTags.add(href,iconUrl,title,requiresConfirmation,requiresConfirmation) : null} + ${taskTags!=null and attrs.contextMenu!='false' ? taskTags.add(href,iconUrl,title,post,requiresConfirmation) : null} + href="${requiresConfirmation || post ? null : href}" iconOnly="true">

\ No newline at end of file -- GitLab From 60632c0e988c6e6620daefa181b24f45c46f8d6c Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 24 Mar 2017 17:12:05 -0400 Subject: [PATCH 0065/2185] Added -strictHostKey option to CLI in -ssh mode. [FIXED JENKINS-33595] Picks up https://github.com/jenkinsci/sshd-module/pull/11 to turn off SSHD by default, but expose it to tests which wish to enable it. --- cli/pom.xml | 2 +- cli/src/main/java/hudson/cli/CLI.java | 18 ++-- .../hudson/cli/client/Messages.properties | 1 + pom.xml | 5 -- test/pom.xml | 6 -- test/src/test/java/hudson/cli/CLITest.java | 87 +++++++++++++++++++ test/src/test/resources/hudson/cli/id_rsa | 27 ++++++ test/src/test/resources/hudson/cli/id_rsa.pub | 1 + war/pom.xml | 2 +- 9 files changed, 131 insertions(+), 18 deletions(-) create mode 100644 test/src/test/java/hudson/cli/CLITest.java create mode 100644 test/src/test/resources/hudson/cli/id_rsa create mode 100644 test/src/test/resources/hudson/cli/id_rsa.pub diff --git a/cli/pom.xml b/cli/pom.xml index dcab55dc12..fb304c5b88 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -60,7 +60,7 @@ org.slf4j - slf4j-nop + slf4j-jdk14 true diff --git a/cli/src/main/java/hudson/cli/CLI.java b/cli/src/main/java/hudson/cli/CLI.java index 7f1ee9b845..e73f329d49 100644 --- a/cli/src/main/java/hudson/cli/CLI.java +++ b/cli/src/main/java/hudson/cli/CLI.java @@ -445,6 +445,7 @@ public class CLI implements AutoCloseable { String user = null; String auth = null; + boolean strictHostKey = false; while(!args.isEmpty()) { String head = args.get(0); @@ -516,6 +517,11 @@ public class CLI implements AutoCloseable { sshAuthRequestedExplicitly = true; continue; } + if (head.equals("-strictHostKey")) { + strictHostKey = true; + args = args.subList(1, args.size()); + continue; + } if (head.equals("-user") && args.size() >= 2) { user = args.get(1); args = args.subList(2, args.size()); @@ -572,7 +578,11 @@ public class CLI implements AutoCloseable { LOGGER.warning("-user required when using -ssh"); return -1; } - return sshConnection(url, user, args, provider); + return sshConnection(url, user, args, provider, strictHostKey); + } + + if (strictHostKey) { + LOGGER.warning("-strictHostKey meaningful only with -ssh"); } if (user != null) { @@ -626,7 +636,7 @@ public class CLI implements AutoCloseable { } } - private static int sshConnection(String jenkinsUrl, String user, List args, PrivateKeyProvider provider) throws IOException { + private static int sshConnection(String jenkinsUrl, String user, List args, PrivateKeyProvider provider, final boolean strictHostKey) throws IOException { URL url = new URL(jenkinsUrl + "/login"); URLConnection conn = url.openConnection(); String endpointDescription = conn.getHeaderField("X-SSH-Endpoint"); @@ -653,10 +663,8 @@ public class CLI implements AutoCloseable { KnownHostsServerKeyVerifier verifier = new DefaultKnownHostsServerKeyVerifier(new ServerKeyVerifier() { @Override public boolean verifyServerKey(ClientSession clientSession, SocketAddress remoteAddress, PublicKey serverKey) { - /** unknown key is okay, but log */ LOGGER.log(Level.WARNING, "Unknown host key for {0}", remoteAddress.toString()); - // TODO should not trust unknown hosts by default; this should be opt-in - return true; + return !strictHostKey; } }, true); diff --git a/cli/src/main/resources/hudson/cli/client/Messages.properties b/cli/src/main/resources/hudson/cli/client/Messages.properties index fdad840f3e..921fe67a21 100644 --- a/cli/src/main/resources/hudson/cli/client/Messages.properties +++ b/cli/src/main/resources/hudson/cli/client/Messages.properties @@ -10,6 +10,7 @@ CLI.Usage=Jenkins CLI\n\ -noCertificateCheck : bypass HTTPS certificate check entirely. Use with caution\n\ -noKeyAuth : don't try to load the SSH authentication private key. Conflicts with -i\n\ -user : specify user (for use with -ssh)\n\ + -strictHostKey : request strict host key checking (for use with -ssh)\n\ -logger FINE : enable detailed logging from the client\n\ -auth [ USER:SECRET | @FILE ] : specify username and either password or API token (or load from them both from a file);\n\ for use with -http, or -remoting but only when the JNLP agent port is disabled\n\ diff --git a/pom.xml b/pom.xml index 6ede36dda0..597ffb8f2c 100644 --- a/pom.xml +++ b/pom.xml @@ -191,11 +191,6 @@ THE SOFTWARE. slf4j-api ${slf4jVersion} - - org.slf4j - slf4j-nop - ${slf4jVersion} - org.slf4j slf4j-jdk14 diff --git a/test/pom.xml b/test/pom.xml index 4e35ee6fd5..53ee628384 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -52,12 +52,6 @@ THE SOFTWARE. jenkins-war ${project.version} war-for-test - - - org.jenkins-ci.modules - sshd - - ${project.groupId} diff --git a/test/src/test/java/hudson/cli/CLITest.java b/test/src/test/java/hudson/cli/CLITest.java new file mode 100644 index 0000000000..f9e252a655 --- /dev/null +++ b/test/src/test/java/hudson/cli/CLITest.java @@ -0,0 +1,87 @@ +/* + * The MIT License + * + * Copyright 2017 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 hudson.cli; + +import hudson.Launcher; +import hudson.model.User; +import hudson.util.StreamTaskListener; +import java.io.ByteArrayOutputStream; +import java.io.File; +import jenkins.model.Jenkins; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import static org.hamcrest.Matchers.containsString; +import org.jenkinsci.main.modules.cli.auth.ssh.UserPropertyImpl; +import org.jenkinsci.main.modules.sshd.SSHD; +import static org.junit.Assert.*; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.MockAuthorizationStrategy; + +public class CLITest { + + @Rule + public JenkinsRule r = new JenkinsRule(); + + @Rule + public TemporaryFolder tmp = new TemporaryFolder(); + + @Issue("JENKINS-41745") + @Test + public void strictHostKey() throws Exception { + File home = tmp.newFolder(); + // Seems it gets created automatically but with inappropriate permissions: + File known_hosts = new File(new File(home, ".ssh"), "known_hosts"); + assertTrue(known_hosts.getParentFile().mkdir()); + assertTrue(known_hosts.createNewFile()); + assertTrue(known_hosts.setWritable(false, false)); + assertTrue(known_hosts.setWritable(true, true)); + r.jenkins.setSecurityRealm(r.createDummySecurityRealm()); + r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to("admin")); + SSHD.get().setPort(0); + File jar = tmp.newFile("jenkins-cli.jar"); + FileUtils.copyURLToFile(r.jenkins.getJnlpJars("jenkins-cli.jar").getURL(), jar); + File privkey = tmp.newFile("id_rsa"); + FileUtils.copyURLToFile(CLITest.class.getResource("id_rsa"), privkey); + User.get("admin").addProperty(new UserPropertyImpl(IOUtils.toString(CLITest.class.getResource("id_rsa.pub")))); + assertNotEquals(0, new Launcher.LocalLauncher(StreamTaskListener.fromStderr()).launch().cmds( + "java", "-Duser.home=" + home, "-jar", jar.getAbsolutePath(), "-s", r.getURL().toString(), "-ssh", "-user", "admin", "-i", privkey.getAbsolutePath(), "-strictHostKey", "who-am-i" + ).stdout(System.out).stderr(System.err).join()); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + assertEquals(0, new Launcher.LocalLauncher(StreamTaskListener.fromStderr()).launch().cmds( + "java", "-Duser.home=" + home, "-jar", jar.getAbsolutePath(), "-s", r.getURL().toString(), "-ssh", "-user", "admin", "-i", privkey.getAbsolutePath(), "who-am-i" + ).stdout(baos).stderr(System.err).join()); + assertThat(baos.toString(), containsString("Authenticated as: admin")); + baos = new ByteArrayOutputStream(); + assertEquals(0, new Launcher.LocalLauncher(StreamTaskListener.fromStderr()).launch().cmds( + "java", "-Duser.home=" + home, "-jar", jar.getAbsolutePath(), "-s", r.getURL().toString(), "-ssh", "-user", "admin", "-i", privkey.getAbsolutePath(), "-strictHostKey", "who-am-i" + ).stdout(baos).stderr(System.err).join()); + assertThat(baos.toString(), containsString("Authenticated as: admin")); + } + +} diff --git a/test/src/test/resources/hudson/cli/id_rsa b/test/src/test/resources/hudson/cli/id_rsa new file mode 100644 index 0000000000..ee2ad6b569 --- /dev/null +++ b/test/src/test/resources/hudson/cli/id_rsa @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAyTqwFqp5Ww2Tr/52D7hhdOwgzYGBUqxrOFopa+kjNEL1Yqwb ++mApUWZ+D3zN9PurhUcVUfeYVXiYWFJ0kG72HIJawL/0BR5oYxRJfumK8Z/sAzAL +xdhc5O5twETrr9gU3cxtvF5oJNP0I9HickAOeC+ZNpiDIIblrhvxXl/QwqrR+/Gv +Nb8TApj+rxXEfNp+N69iGnnxzWn1FeKeOAWpwoBAxZNoqBQAFacF7xfQnoygyekC +xk+ts2O5Zzv8iJ10sVf+x2Q79rxAtsc0xOGhZbBAzbmFTz0PE4iWuo/Vo1c6mM7u +/dam+FxB2NqPNw7W+4eiCnEVkiQZlrxmuGvK7wIDAQABAoIBACml1+QZDFzoBnUa +eVzvkFwesvtVnmp5/QcAwinvarXaVedCL9g2JtcOG3EhJ49YtzsyZxs7329xMja1 +eiKalJ157UaPc/XLQVegT0XRGEzCCJrwSr979F39awGsQgt28XqmYN/nui5FH/Z5 +7iAvWc9OKqu+DQWiZc8PQXmC4zYmvhGQ8vKx44RSqlWCjd9IqBVhpE5gxpI/SmCx +umUNNtoH0hBWr+MsVHzr6UUrC3a99+7bB4We8XMXXFLzbTUSgiYFmK+NxPs/Fux/ +IAyXAMbDw2HeqZ7g4kTaf4cvmVOwhh4zlvB4p7j301LdO1jmvs9z0fn/QJcTpVM7 +ISMKwAECgYEA/uKVdmOKTk3dKzKRFXtWJjqypOXakoX+25lUcVv2PXYRr8Sln9jC +A13fbhvwq+FqbdnNlB23ag5niCVLfUpB1DYYP5jd4lU8D6HZQiHlmokB6nLT9NIW +iTcG88E58Bta/l1Ue5Yn+LqluBC4i289wFbH1kZyxQ565s5dJEv9uAECgYEAyhwF +ZOqTK2lZe5uuN4owVLQaYFj9fsdFHULzlK/UAtkG1gCJhjBmwSEpZFFMH6WgwHk5 +SHJEom0uB4qRv8gQcxl9OSiDsp56ymr0NBhlPVXWr6IzLotLy5XBC1muqvYYlj7E +kHgSet/h8RUM/FeEiwOFHDU2DkMb8Qx1hfMdAu8CgYBSEsYL9CuB4WK5WTQMlcV8 +0+PYY0dJbSpOrgXZ5sHYsp8pWQn3+cUnbl/WxdpujkxGCR9AdX0tAmxmE5RGSNX/ +rleKiv/PtKB9bCFYQS/83ecnBkioCcpF7tknPm4YmcZoJ8dfcE94sSlRpti11WEu +AQOiRNcKCwqaLZMib/HIAQKBgQCdiOffeERMYypfgcJzAiCX9WZV0SeOCS7jFwub +ys17hsSgS/zl/pYpVXrY+dFXHZfGTvcKdB7xaB6nvCfND9lajfSgd+bndEYLvwAo +Fxfajizv64LvdZ4XytuUyEuwcHBLtBMs9Jqa8iU/8AOWMXVbkdvQV92RkleWNPrp +9MyZOwKBgQD9x8MnX5LVBfQKuL9qX6l9Da06EyMkzfz3obKn9AAJ3Xj9+45TNPJu +HnZyvJWesl1vDjXQTm+PVkdyE0WQgoiVX+wxno0hsoly5Uqb5EYHtTUrZzRpkyLK +1VmtDxT5D8gorUgn6crzk4PKaxRkPfAimZdlkQm6iOtuR3kqn5BtIQ== +-----END RSA PRIVATE KEY----- diff --git a/test/src/test/resources/hudson/cli/id_rsa.pub b/test/src/test/resources/hudson/cli/id_rsa.pub new file mode 100644 index 0000000000..91f8ff7180 --- /dev/null +++ b/test/src/test/resources/hudson/cli/id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJOrAWqnlbDZOv/nYPuGF07CDNgYFSrGs4Wilr6SM0QvVirBv6YClRZn4PfM30+6uFRxVR95hVeJhYUnSQbvYcglrAv/QFHmhjFEl+6Yrxn+wDMAvF2Fzk7m3AROuv2BTdzG28Xmgk0/Qj0eJyQA54L5k2mIMghuWuG/FeX9DCqtH78a81vxMCmP6vFcR82n43r2IaefHNafUV4p44BanCgEDFk2ioFAAVpwXvF9CejKDJ6QLGT62zY7lnO/yInXSxV/7HZDv2vEC2xzTE4aFlsEDNuYVPPQ8TiJa6j9WjVzqYzu791qb4XEHY2o83Dtb7h6IKcRWSJBmWvGa4a8rv your_email@example.com diff --git a/war/pom.xml b/war/pom.xml index b9db207388..dca464d82f 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -134,7 +134,7 @@ THE SOFTWARE. org.jenkins-ci.modules sshd - 1.11-20170315.153852-1 + 1.11-20170324.200647-2 org.jenkins-ci.ui -- GitLab From c6a23bdd264dff17b681765b08cf7f439f69d41e Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 29 Mar 2017 13:49:23 -0700 Subject: [PATCH 0066/2185] [maven-release-plugin] prepare release jenkins-2.46.1 --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index 5e08491262..48e171f624 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main pom - 2.46.1-SNAPSHOT + 2.46.1 cli diff --git a/core/pom.xml b/core/pom.xml index bde46c6fcf..bf61d83c74 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.46.1-SNAPSHOT + 2.46.1 jenkins-core diff --git a/pom.xml b/pom.xml index d4d9c7734c..de2f76e8df 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.46.1-SNAPSHOT + 2.46.1 pom Jenkins main module @@ -58,7 +58,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - HEAD + jenkins-2.46.1 diff --git a/test/pom.xml b/test/pom.xml index 53cdb1bdd8..4e38159462 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.46.1-SNAPSHOT + 2.46.1 test diff --git a/war/pom.xml b/war/pom.xml index 1c6614e045..97eb88b678 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.46.1-SNAPSHOT + 2.46.1 jenkins-war -- GitLab From b71b1fb43b4ff896420455251e8c21af90e08ad9 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 29 Mar 2017 13:49:23 -0700 Subject: [PATCH 0067/2185] [maven-release-plugin] prepare for next development iteration --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index 48e171f624..22938200fe 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main pom - 2.46.1 + 2.46.2-SNAPSHOT cli diff --git a/core/pom.xml b/core/pom.xml index bf61d83c74..d16eb70615 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.46.1 + 2.46.2-SNAPSHOT jenkins-core diff --git a/pom.xml b/pom.xml index de2f76e8df..5ef6dbee19 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.46.1 + 2.46.2-SNAPSHOT pom Jenkins main module @@ -58,7 +58,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - jenkins-2.46.1 + HEAD diff --git a/test/pom.xml b/test/pom.xml index 4e38159462..06d28b54a3 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.46.1 + 2.46.2-SNAPSHOT test diff --git a/war/pom.xml b/war/pom.xml index 97eb88b678..7a9eebff51 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.46.1 + 2.46.2-SNAPSHOT jenkins-war -- GitLab From 43d612f984572bfd764009b0a46184cb535f97ce Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Thu, 30 Mar 2017 15:26:08 +0200 Subject: [PATCH 0068/2185] [FIX JENKINS-43228] Consider time zone for cron validation --- core/src/main/java/hudson/scheduler/CronTab.java | 13 +++++++++++++ .../src/main/java/hudson/scheduler/CronTabList.java | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/hudson/scheduler/CronTab.java b/core/src/main/java/hudson/scheduler/CronTab.java index 81fa5075c9..40381e2b71 100644 --- a/core/src/main/java/hudson/scheduler/CronTab.java +++ b/core/src/main/java/hudson/scheduler/CronTab.java @@ -532,4 +532,17 @@ public final class CronTab { return null; } } + + /** + * Returns the configured time zone, or null if none is configured + * + * @return the configured time zone, or null if none is configured + * @since TODO + */ + @CheckForNull public TimeZone getTimeZone() { + if (this.specTimezone == null) { + return null; + } + return TimeZone.getTimeZone(this.specTimezone); + } } diff --git a/core/src/main/java/hudson/scheduler/CronTabList.java b/core/src/main/java/hudson/scheduler/CronTabList.java index 3243dda38e..bb20834597 100644 --- a/core/src/main/java/hudson/scheduler/CronTabList.java +++ b/core/src/main/java/hudson/scheduler/CronTabList.java @@ -131,7 +131,7 @@ public final class CronTabList { public @CheckForNull Calendar previous() { Calendar nearest = null; for (CronTab tab : tabs) { - Calendar scheduled = tab.floor(Calendar.getInstance()); + Calendar scheduled = tab.floor(tab.getTimeZone() == null ? Calendar.getInstance() : Calendar.getInstance(tab.getTimeZone())); if (nearest == null || nearest.before(scheduled)) { nearest = scheduled; } @@ -143,7 +143,7 @@ public final class CronTabList { public @CheckForNull Calendar next() { Calendar nearest = null; for (CronTab tab : tabs) { - Calendar scheduled = tab.ceil(Calendar.getInstance()); + Calendar scheduled = tab.ceil(tab.getTimeZone() == null ? Calendar.getInstance() : Calendar.getInstance(tab.getTimeZone())); if (nearest == null || nearest.after(scheduled)) { nearest = scheduled; } -- GitLab From a34479c418cd6be5c83bf5cd36121e9c42332da3 Mon Sep 17 00:00:00 2001 From: Daniel Beck Date: Thu, 30 Mar 2017 18:11:52 +0200 Subject: [PATCH 0069/2185] [JENKINS-43228] Add test --- .../hudson/triggers/TimerTriggerTest.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/core/src/test/java/hudson/triggers/TimerTriggerTest.java b/core/src/test/java/hudson/triggers/TimerTriggerTest.java index ef6edf4400..8489776eee 100644 --- a/core/src/test/java/hudson/triggers/TimerTriggerTest.java +++ b/core/src/test/java/hudson/triggers/TimerTriggerTest.java @@ -24,9 +24,14 @@ package hudson.triggers; import antlr.ANTLRException; +import hudson.scheduler.CronTabList; +import hudson.scheduler.Hash; +import org.junit.Assert; import org.junit.Test; import org.jvnet.hudson.test.Issue; +import java.util.TimeZone; + /** * @author Kanstantsin Shautsou */ @@ -36,4 +41,22 @@ public class TimerTriggerTest { public void testNoNPE() throws ANTLRException { new TimerTrigger("").run(); } + + @Issue("JENKINS-43328") + @Test + public void testTimeZoneOffset() throws Exception { + TimeZone defaultTz = TimeZone.getDefault(); + TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin")); + try { + String cron = "TZ=GMT\nH 0 * * *"; + CronTabList ctl = CronTabList.create(cron, Hash.from("whatever")); + Assert.assertEquals("previous occurrence is in GMT", "GMT", ctl.previous().getTimeZone().getID()); + + cron = "TZ=America/Denver\nH 0 * * *"; + ctl = CronTabList.create(cron, Hash.from("whatever")); + Assert.assertEquals("next occurrence is in America/Denver", "America/Denver", ctl.next().getTimeZone().getID()); + } finally { + TimeZone.setDefault(defaultTz); + } + } } -- GitLab From a3bf6a9801a755c2b62e9b11e513b5cc616d3e47 Mon Sep 17 00:00:00 2001 From: kzantow Date: Thu, 30 Mar 2017 15:32:14 -0400 Subject: [PATCH 0070/2185] JENKINS-41778 - setup wizard issues when failures --- .../main/java/hudson/model/UpdateCenter.java | 5 ++- war/src/main/js/pluginSetupWizardGui.js | 42 +++++++++++++++---- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/hudson/model/UpdateCenter.java b/core/src/main/java/hudson/model/UpdateCenter.java index 98e9426bf4..763bdd57a6 100644 --- a/core/src/main/java/hudson/model/UpdateCenter.java +++ b/core/src/main/java/hudson/model/UpdateCenter.java @@ -1934,8 +1934,11 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas throw new RuntimeException(e); } } + // Must check for success, otherwise may have failed installation + if (ij.status instanceof Success) { + return true; + } } - return true; } } } diff --git a/war/src/main/js/pluginSetupWizardGui.js b/war/src/main/js/pluginSetupWizardGui.js index ef33b7cdf4..15711c8baa 100644 --- a/war/src/main/js/pluginSetupWizardGui.js +++ b/war/src/main/js/pluginSetupWizardGui.js @@ -461,9 +461,34 @@ var createPluginSetupWizard = function(appendTarget) { setPanel(pluginSuccessPanel, { installingPlugins : installingPlugins, failedPlugins: true }); return; } - + + var attachScrollEvent = function() { + var $c = $('.install-console-scroll'); + if (!$c.length) { + setTimeout(attachScrollEvent, 50); + return; + } + var events = $._data($c[0], "events"); + if (!events || !events.scroll) { + $c.on('scroll', function() { + if (!$c.data('wasAutoScrolled')) { + var top = $c[0].scrollHeight - $c.height(); + if ($c.scrollTop() === top) { + // resume auto-scroll + $c.data('userScrolled', false); + } else { + // user scrolled up + $c.data('userScrolled', true); + } + } else { + $c.data('wasAutoScrolled', false); + } + }); + } + }; + initInstallingPluginList(); - setPanel(progressPanel, { installingPlugins : installingPlugins }); + setPanel(progressPanel, { installingPlugins : installingPlugins }, attachScrollEvent); // call to the installStatus, update progress bar & plugin details; transition on complete var updateStatus = function() { @@ -491,8 +516,8 @@ var createPluginSetupWizard = function(appendTarget) { $('.progress-bar').css({width: ((100.0 * complete)/total) + '%'}); // update details - var $c = $('.install-text'); - $c.children().remove(); + var $txt = $('.install-text'); + $txt.children().remove(); for(i = 0; i < jobs.length; i++) { j = jobs[i]; @@ -538,7 +563,7 @@ var createPluginSetupWizard = function(appendTarget) { else { $div.addClass('dependent'); } - $c.append($div); + $txt.append($div); var $itemProgress = $('.selected-plugin[id="installing-' + jenkins.idIfy(j.name) + '"]'); if($itemProgress.length > 0 && !$itemProgress.is('.'+state)) { @@ -547,13 +572,14 @@ var createPluginSetupWizard = function(appendTarget) { } } - $c = $('.install-console-scroll'); - if($c.is(':visible')) { + var $c = $('.install-console-scroll'); + if($c && $c.is(':visible') && !$c.data('userScrolled')) { + $c.data('wasAutoScrolled', true); $c.scrollTop($c[0].scrollHeight); } // keep polling while install is running - if(complete < total || data.state === 'INITIAL_PLUGINS_INSTALLING') { + if(complete < total && data.state === 'INITIAL_PLUGINS_INSTALLING') { setPanel(progressPanel, { installingPlugins : installingPlugins }); // wait a sec setTimeout(updateStatus, 250); -- GitLab From f1689367c30f9199b307ca56771a9f86cc599c18 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 3 Apr 2017 17:06:53 -0400 Subject: [PATCH 0071/2185] =?UTF-8?q?Test=20failure=20on=20Windows,=20perh?= =?UTF-8?q?aps=20specific=20to=20filesystem=20type=20of=20TemporaryFolder.?= =?UTF-8?q?=20java.nio.file.FileSystemException:=20=E2=80=A6\.ssh\known=5F?= =?UTF-8?q?hosts:=20Incorrect=20function.=20=20=20=20=20=20=20=20=20at=20s?= =?UTF-8?q?un.nio.fs.WindowsException.translateToIOException(WindowsExcept?= =?UTF-8?q?ion.java:86)=20=20=20=20=20=20=20=20=20at=20sun.nio.fs.WindowsE?= =?UTF-8?q?xception.rethrowAsIOException(WindowsException.java:90)=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20at=20sun.nio.fs.WindowsAclFileAttributeVie?= =?UTF-8?q?w.getFileSecurity(WindowsAclFileAttributeView.java:88)=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20at=20sun.nio.fs.WindowsAclFileAttributeView.g?= =?UTF-8?q?etOwner(WindowsAclFileAttributeView.java:121)=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20at=20sun.nio.fs.FileOwnerAttributeViewImpl.getOwner(Fi?= =?UTF-8?q?leOwnerAttributeViewImpl.java:91)=20=20=20=20=20=20=20=20=20at?= =?UTF-8?q?=20java.nio.file.Files.getOwner(Files.java:2079)=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20at=20org.apache.sshd.common.util.io.IoUtils.getFile?= =?UTF-8?q?Owner(IoUtils.java:272)=20=20=20=20=20=20=20=20=20at=20org.apac?= =?UTF-8?q?he.sshd.common.util.io.ModifiableFileWatcher.validateStrictConf?= =?UTF-8?q?igFilePermissions(ModifiableFileWatcher.java:240)=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20at=20org.apache.sshd.client.keyverifier.DefaultK?= =?UTF-8?q?nownHostsServerKeyVerifier.reloadKnownHosts(DefaultKnownHostsSe?= =?UTF-8?q?rverKeyVerifier.java:79)=20=20=20=20=20=20=20=20=20at=20org.apa?= =?UTF-8?q?che.sshd.client.keyverifier.KnownHostsServerKeyVerifier.verifyS?= =?UTF-8?q?erverKey(KnownHostsServerKeyVerifier.java:161)=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20at=20=E2=80=A6=20Interactive=20testing=20of=20known?= =?UTF-8?q?=5Fhosts=20on=20XP=20works=20as=20expected.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cli/src/main/java/hudson/cli/CLI.java | 2 +- test/src/test/java/hudson/cli/CLITest.java | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/cli/src/main/java/hudson/cli/CLI.java b/cli/src/main/java/hudson/cli/CLI.java index e73f329d49..6f5db5de9d 100644 --- a/cli/src/main/java/hudson/cli/CLI.java +++ b/cli/src/main/java/hudson/cli/CLI.java @@ -541,7 +541,7 @@ public class CLI implements AutoCloseable { Level level = parse(args.get(1)); ConsoleHandler h = new ConsoleHandler(); h.setLevel(level); - for (Logger logger : new Logger[] {LOGGER, PlainCLIProtocol.LOGGER}) { // perhaps also Channel + for (Logger logger : new Logger[] {LOGGER, PlainCLIProtocol.LOGGER, Logger.getLogger("org.apache.sshd")}) { // perhaps also Channel logger.setLevel(level); logger.addHandler(h); } diff --git a/test/src/test/java/hudson/cli/CLITest.java b/test/src/test/java/hudson/cli/CLITest.java index f9e252a655..6a8ddcf4f3 100644 --- a/test/src/test/java/hudson/cli/CLITest.java +++ b/test/src/test/java/hudson/cli/CLITest.java @@ -29,6 +29,8 @@ import hudson.model.User; import hudson.util.StreamTaskListener; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import jenkins.model.Jenkins; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; @@ -36,6 +38,7 @@ import static org.hamcrest.Matchers.containsString; import org.jenkinsci.main.modules.cli.auth.ssh.UserPropertyImpl; import org.jenkinsci.main.modules.sshd.SSHD; import static org.junit.Assert.*; +import static org.junit.Assume.*; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -57,10 +60,15 @@ public class CLITest { File home = tmp.newFolder(); // Seems it gets created automatically but with inappropriate permissions: File known_hosts = new File(new File(home, ".ssh"), "known_hosts"); - assertTrue(known_hosts.getParentFile().mkdir()); - assertTrue(known_hosts.createNewFile()); - assertTrue(known_hosts.setWritable(false, false)); - assertTrue(known_hosts.setWritable(true, true)); + assumeTrue(known_hosts.getParentFile().mkdir()); + assumeTrue(known_hosts.createNewFile()); + assumeTrue(known_hosts.setWritable(false, false)); + assumeTrue(known_hosts.setWritable(true, true)); + try { + Files.getOwner(known_hosts.toPath()); + } catch (IOException x) { + assumeNoException("Sometimes on Windows KnownHostsServerKeyVerifier.acceptIncompleteHostKeys says WARNING: Failed (FileSystemException) to reload server keys from …\\\\.ssh\\\\known_hosts: … Incorrect function.", x); + } r.jenkins.setSecurityRealm(r.createDummySecurityRealm()); r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to("admin")); SSHD.get().setPort(0); @@ -74,7 +82,7 @@ public class CLITest { ).stdout(System.out).stderr(System.err).join()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); assertEquals(0, new Launcher.LocalLauncher(StreamTaskListener.fromStderr()).launch().cmds( - "java", "-Duser.home=" + home, "-jar", jar.getAbsolutePath(), "-s", r.getURL().toString(), "-ssh", "-user", "admin", "-i", privkey.getAbsolutePath(), "who-am-i" + "java", "-Duser.home=" + home, "-jar", jar.getAbsolutePath(), "-s", r.getURL().toString(), "-ssh", "-user", "admin", "-i", privkey.getAbsolutePath(), "-logger", "FINEST", "who-am-i" ).stdout(baos).stderr(System.err).join()); assertThat(baos.toString(), containsString("Authenticated as: admin")); baos = new ByteArrayOutputStream(); -- GitLab From 551808ca6ae7540a489173be09165369c4e4c743 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 3 Apr 2017 17:57:19 -0400 Subject: [PATCH 0072/2185] Using kill -QUIT to try to diagnose client hangs observed on ci.jenkins.io but not locally reproducible. --- .../test/java/hudson/cli/CLIActionTest.java | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/test/src/test/java/hudson/cli/CLIActionTest.java b/test/src/test/java/hudson/cli/CLIActionTest.java index 5c317ce41c..7333c3418b 100644 --- a/test/src/test/java/hudson/cli/CLIActionTest.java +++ b/test/src/test/java/hudson/cli/CLIActionTest.java @@ -7,14 +7,17 @@ import com.gargoylesoftware.htmlunit.WebResponse; import com.google.common.collect.Lists; import hudson.Functions; import hudson.Launcher; +import hudson.Proc; import hudson.model.Item; import hudson.model.User; import hudson.remoting.Channel; import hudson.remoting.ChannelBuilder; +import hudson.util.ProcessTree; import hudson.util.StreamTaskListener; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.lang.reflect.Field; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; @@ -23,8 +26,10 @@ import java.util.List; import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import jenkins.model.Jenkins; import jenkins.security.ApiTokenProperty; +import jenkins.util.Timer; import org.apache.commons.io.FileUtils; import org.codehaus.groovy.runtime.Security218; import static org.hamcrest.Matchers.containsString; @@ -198,7 +203,29 @@ public class CLIActionTest { commands.add(ADMIN + ":" + User.get(ADMIN).getProperty(ApiTokenProperty.class).getApiToken()); } commands.addAll(Arrays.asList(args)); - assertEquals(code, new Launcher.LocalLauncher(StreamTaskListener.fromStderr()).launch().cmds(commands).stdout(System.out).stderr(System.err).join()); + final Launcher.LocalLauncher launcher = new Launcher.LocalLauncher(StreamTaskListener.fromStderr()); + final Proc proc = launcher.launch().cmds(commands).stdout(System.out).stderr(System.err).start(); + if (!Functions.isWindows()) { + // Try to get a thread dump of the client if it hangs. + Timer.get().schedule(new Runnable() { + @Override + public void run() { + try { + if (proc.isAlive()) { + Field procF = Proc.LocalProc.class.getDeclaredField("proc"); + procF.setAccessible(true); + ProcessTree.OSProcess osp = ProcessTree.get().get((Process) procF.get(proc)); + if (osp != null) { + launcher.launch().cmds("kill", "-QUIT", Integer.toString(osp.getPid())).stdout(System.out).stderr(System.err).join(); + } + } + } catch (Exception x) { + throw new AssertionError(x); + } + } + }, 1, TimeUnit.MINUTES); + } + assertEquals(code, proc.join()); } @Issue("JENKINS-41745") -- GitLab From f6314b7bfb0a8d4f8c7a71e6271100cc8d0e46f7 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 3 Apr 2017 18:37:59 -0400 Subject: [PATCH 0073/2185] Testing interrupt behavior. --- test/src/test/java/hudson/cli/CLITest.java | 46 ++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/test/src/test/java/hudson/cli/CLITest.java b/test/src/test/java/hudson/cli/CLITest.java index 6a8ddcf4f3..f774de58e1 100644 --- a/test/src/test/java/hudson/cli/CLITest.java +++ b/test/src/test/java/hudson/cli/CLITest.java @@ -24,29 +24,42 @@ package hudson.cli; +import com.google.common.collect.Lists; import hudson.Launcher; +import hudson.Proc; +import hudson.model.FreeStyleProject; import hudson.model.User; import hudson.util.StreamTaskListener; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; import jenkins.model.Jenkins; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.apache.commons.io.output.TeeOutputStream; import static org.hamcrest.Matchers.containsString; import org.jenkinsci.main.modules.cli.auth.ssh.UserPropertyImpl; import org.jenkinsci.main.modules.sshd.SSHD; import static org.junit.Assert.*; import static org.junit.Assume.*; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.jvnet.hudson.test.BuildWatcher; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.MockAuthorizationStrategy; +import org.jvnet.hudson.test.SleepBuilder; public class CLITest { + + @ClassRule + public static BuildWatcher buildWatcher = new BuildWatcher(); @Rule public JenkinsRule r = new JenkinsRule(); @@ -92,4 +105,37 @@ public class CLITest { assertThat(baos.toString(), containsString("Authenticated as: admin")); } + @Issue("JENKINS-41745") + @Test + public void interrupt() throws Exception { + r.jenkins.setSecurityRealm(r.createDummySecurityRealm()); + r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to("admin")); + SSHD.get().setPort(0); + File jar = tmp.newFile("jenkins-cli.jar"); + FileUtils.copyURLToFile(r.jenkins.getJnlpJars("jenkins-cli.jar").getURL(), jar); + File privkey = tmp.newFile("id_rsa"); + FileUtils.copyURLToFile(CLITest.class.getResource("id_rsa"), privkey); + User.get("admin").addProperty(new UserPropertyImpl(IOUtils.toString(CLITest.class.getResource("id_rsa.pub")))); + FreeStyleProject p = r.createFreeStyleProject("p"); + p.getBuildersList().add(new SleepBuilder(TimeUnit.MINUTES.toMillis(5))); + doInterrupt(jar, p, "-remoting", "-i", privkey.getAbsolutePath()); + doInterrupt(jar, p, "-ssh", "-user", "admin", "-i", privkey.getAbsolutePath()); + /* TODO does not yet work in HTTP mode: + doInterrupt(jar, p, "-http", "-auth", "admin:admin"); + */ + } + private void doInterrupt(File jar, FreeStyleProject p, String... modeArgs) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + List args = Lists.newArrayList("java", "-jar", jar.getAbsolutePath(), "-s", r.getURL().toString()); + args.addAll(Arrays.asList(modeArgs)); + args.addAll(Arrays.asList("build", "-s", "-v", "p")); + Proc proc = new Launcher.LocalLauncher(StreamTaskListener.fromStderr()).launch().cmds(args).stdout(new TeeOutputStream(baos, System.out)).stderr(System.err).start(); + while (!baos.toString().contains("Sleeping ")) { + Thread.sleep(100); + } + System.err.println("Killing client"); + proc.kill(); + r.waitForCompletion(p.getLastBuild()); + } + } -- GitLab From 9812db70d3a161669d31b00531524ff9cd715e25 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 3 Apr 2017 18:52:59 -0400 Subject: [PATCH 0074/2185] Improved handling of stream closure. Making interrupt of `build -s` work in `-http` mode. Does not solve `ReadPendingException`s. --- cli/src/main/java/hudson/cli/CLI.java | 51 ++++---- .../java/hudson/cli/PlainCLIProtocol.java | 112 +++++++++++------- core/src/main/java/hudson/cli/CLIAction.java | 64 ++++++---- test/src/test/java/hudson/cli/CLITest.java | 2 - 4 files changed, 140 insertions(+), 89 deletions(-) diff --git a/cli/src/main/java/hudson/cli/CLI.java b/cli/src/main/java/hudson/cli/CLI.java index 6f5db5de9d..e1b64fd3c5 100644 --- a/cli/src/main/java/hudson/cli/CLI.java +++ b/cli/src/main/java/hudson/cli/CLI.java @@ -728,34 +728,39 @@ public class CLI implements AutoCloseable { protected void onStderr(byte[] chunk) throws IOException { System.err.write(chunk); } - } - final ClientSideImpl connection = new ClientSideImpl(streams.getInputStream(), streams.getOutputStream()); - for (String arg : args) { - connection.sendArg(arg); - } - connection.sendEncoding(Charset.defaultCharset().name()); - connection.sendLocale(Locale.getDefault().toString()); - connection.sendStart(); - connection.begin(); - final OutputStream stdin = connection.streamStdin(); - new Thread("input reader") { @Override - public void run() { - try { - int c; - while ((c = System.in.read()) != -1) { // TODO use InputStream.available - stdin.write(c); + protected synchronized void handleClose() { + notifyAll(); + } + } + try (final ClientSideImpl connection = new ClientSideImpl(streams.getInputStream(), streams.getOutputStream())) { + for (String arg : args) { + connection.sendArg(arg); + } + connection.sendEncoding(Charset.defaultCharset().name()); + connection.sendLocale(Locale.getDefault().toString()); + connection.sendStart(); + connection.begin(); + final OutputStream stdin = connection.streamStdin(); + new Thread("input reader") { + @Override + public void run() { + try { + int c; + while ((c = System.in.read()) != -1) { // TODO use InputStream.available + stdin.write(c); + } + connection.sendEndStdin(); + } catch (IOException x) { + x.printStackTrace(); } - connection.sendEndStdin(); - } catch (IOException x) { - x.printStackTrace(); } + }.start(); + synchronized (connection) { + connection.wait(); } - }.start(); - synchronized (connection) { - connection.wait(); + return connection.exit; } - return connection.exit; } private static String computeVersion() { diff --git a/cli/src/main/java/hudson/cli/PlainCLIProtocol.java b/cli/src/main/java/hudson/cli/PlainCLIProtocol.java index 78dc64f276..db55ff105d 100644 --- a/cli/src/main/java/hudson/cli/PlainCLIProtocol.java +++ b/cli/src/main/java/hudson/cli/PlainCLIProtocol.java @@ -25,12 +25,14 @@ package hudson.cli; import java.io.ByteArrayOutputStream; +import java.io.Closeable; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.channels.ReadPendingException; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.io.IOUtils; @@ -68,7 +70,7 @@ class PlainCLIProtocol { STDERR } - static abstract class EitherSide { + static abstract class EitherSide implements Closeable { private final CountingInputStream cis; private final FlightRecorderInputStream flightRecorder; @@ -83,51 +85,71 @@ class PlainCLIProtocol { } final void begin() { - new Thread("PlainCLIProtocol") { // TODO set distinctive Thread.name - @Override - public void run() { - try { - while (true) { - LOGGER.finest("reading frame"); - int framelen; - try { - framelen = dis.readInt(); - } catch (EOFException x) { - break; // TODO verify that we hit EOF immediately, not partway into framelen - } - if (framelen < 0) { - throw new IOException("corrupt stream: negative frame length"); - } - byte b = dis.readByte(); - if (b < 0) { // i.e., >127 - throw new IOException("corrupt stream: negative operation code"); - } - if (b >= Op.values().length) { - LOGGER.log(Level.WARNING, "unknown operation #{0}: {1}", new Object[] {b, HexDump.toHex(flightRecorder.getRecord())}); - IOUtils.skipFully(dis, framelen); - continue; - } - Op op = Op.values()[b]; - long start = cis.getByteCount(); - LOGGER.log(Level.FINEST, "handling frame with {0} of length {1}", new Object[] {op, framelen}); - boolean handled = handle(op, framelen); - if (handled) { - long actuallyRead = cis.getByteCount() - start; - if (actuallyRead != framelen) { - throw new IOException("corrupt stream: expected to read " + framelen + " bytes from " + op + " but read " + actuallyRead); - } - } else { - LOGGER.log(Level.WARNING, "unexpected {0}: {1}", new Object[] {op, HexDump.toHex(flightRecorder.getRecord())}); - IOUtils.skipFully(dis, framelen); + new Reader().start(); + } + + private class Reader extends Thread { + + Reader() { + super("PlainCLIProtocol"); // TODO set distinctive Thread.name + } + + @Override + public void run() { + try { + while (true) { + LOGGER.finest("reading frame"); + int framelen; + try { + framelen = dis.readInt(); + } catch (EOFException x) { + handleClose(); + break; // TODO verify that we hit EOF immediately, not partway into framelen + } catch (ReadPendingException x) { + LOGGER.log(Level.FINE, null, x); + // TODO what does this signify? Seems to be thrown randomly by org.eclipse.jetty.io.FillInterest.register. No obvious impact. + // Check https://github.com/eclipse/jetty.project/issues/1047 in 9.4.3.v20170317 but cf. https://github.com/joakime/jetty-async-bug/issues/1 + handleClose(); + break; + } + if (framelen < 0) { + throw new IOException("corrupt stream: negative frame length"); + } + byte b = dis.readByte(); + if (b < 0) { // i.e., >127 + throw new IOException("corrupt stream: negative operation code"); + } + if (b >= Op.values().length) { + LOGGER.log(Level.WARNING, "unknown operation #{0}: {1}", new Object[] {b, HexDump.toHex(flightRecorder.getRecord())}); + IOUtils.skipFully(dis, framelen); + continue; + } + Op op = Op.values()[b]; + long start = cis.getByteCount(); + LOGGER.log(Level.FINEST, "handling frame with {0} of length {1}", new Object[] {op, framelen}); + boolean handled = handle(op, framelen); + if (handled) { + long actuallyRead = cis.getByteCount() - start; + if (actuallyRead != framelen) { + throw new IOException("corrupt stream: expected to read " + framelen + " bytes from " + op + " but read " + actuallyRead); } + } else { + LOGGER.log(Level.WARNING, "unexpected {0}: {1}", new Object[] {op, HexDump.toHex(flightRecorder.getRecord())}); + IOUtils.skipFully(dis, framelen); + } } - } catch (IOException x) { - LOGGER.log(Level.WARNING, null, flightRecorder.analyzeCrash(x, "broken stream")); - } + } catch (IOException x) { + LOGGER.log(Level.WARNING, null, flightRecorder.analyzeCrash(x, "broken stream")); + } catch (ReadPendingException x) { // as above + LOGGER.log(Level.FINE, null, x); + handleClose(); } - }.start(); + } + } + protected abstract void handleClose(); + protected abstract boolean handle(Op op, int framelen) throws IOException; private void writeOp(Op op) throws IOException { @@ -165,6 +187,7 @@ class PlainCLIProtocol { } protected final byte[] readChunk(int framelen) throws IOException { + assert Thread.currentThread() instanceof EitherSide.Reader; byte[] buf = new byte[framelen]; dis.readFully(buf); return buf; @@ -187,6 +210,11 @@ class PlainCLIProtocol { }; } + @Override + public synchronized void close() throws IOException { + dos.close(); + } + } static abstract class ServerSide extends EitherSide { @@ -197,6 +225,7 @@ class PlainCLIProtocol { @Override protected final boolean handle(Op op, int framelen) throws IOException { + assert Thread.currentThread() instanceof EitherSide.Reader; switch (op) { case ARG: onArg(dis.readUTF()); @@ -255,6 +284,7 @@ class PlainCLIProtocol { @Override protected boolean handle(Op op, int framelen) throws IOException { + assert Thread.currentThread() instanceof EitherSide.Reader; switch (op) { case EXIT: onExit(dis.readInt()); diff --git a/core/src/main/java/hudson/cli/CLIAction.java b/core/src/main/java/hudson/cli/CLIAction.java index 6651253c17..15fefb75bb 100644 --- a/core/src/main/java/hudson/cli/CLIAction.java +++ b/core/src/main/java/hudson/cli/CLIAction.java @@ -55,6 +55,7 @@ import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import java.util.logging.Logger; import jenkins.util.FullDuplexHttpService; @@ -132,6 +133,7 @@ public class CLIAction implements UnprotectedRootAction, StaplerProxy { return new FullDuplexHttpService(uuid) { @Override protected void run(InputStream upload, OutputStream download) throws IOException, InterruptedException { + final AtomicReference runningThread = new AtomicReference<>(); class ServerSideImpl extends PlainCLIProtocol.ServerSide { List args = new ArrayList<>(); Locale locale = Locale.getDefault(); @@ -176,30 +178,46 @@ public class CLIAction implements UnprotectedRootAction, StaplerProxy { protected void onEndStdin() throws IOException { stdinMatch.close(); } + @Override + protected synchronized void handleClose() { + notifyAll(); + Thread t = runningThread.get(); + if (t != null) { + t.interrupt(); + } + } } - ServerSideImpl connection = new ServerSideImpl(upload, download); - connection.begin(); - synchronized (connection) { - connection.wait(); // TODO this can wait indefinitely even when the connection is broken - } - PrintStream stdout = new PrintStream(connection.streamStdout(), false, connection.encoding.name()); - PrintStream stderr = new PrintStream(connection.streamStderr(), true, connection.encoding.name()); - String commandName = connection.args.get(0); - CLICommand command = CLICommand.clone(commandName); - if (command == null) { - stderr.println("No such command " + commandName); - connection.sendExit(2); - return; - } - command.setTransportAuth(Jenkins.getAuthentication()); - command.setClientCharset(connection.encoding); - CLICommand orig = CLICommand.setCurrent(command); - try { - int exit = command.main(connection.args.subList(1, connection.args.size()), connection.locale, connection.stdin, stdout, stderr); - stdout.flush(); - connection.sendExit(exit); - } finally { - CLICommand.setCurrent(orig); + try (ServerSideImpl connection = new ServerSideImpl(upload, download)) { + connection.begin(); + synchronized (connection) { + connection.wait(); + } + PrintStream stdout = new PrintStream(connection.streamStdout(), false, connection.encoding.name()); + PrintStream stderr = new PrintStream(connection.streamStderr(), true, connection.encoding.name()); + if (connection.args.isEmpty()) { + stderr.println("Connection closed before arguments received"); + connection.sendExit(2); + return; + } + String commandName = connection.args.get(0); + CLICommand command = CLICommand.clone(commandName); + if (command == null) { + stderr.println("No such command " + commandName); + connection.sendExit(2); + return; + } + command.setTransportAuth(Jenkins.getAuthentication()); + command.setClientCharset(connection.encoding); + CLICommand orig = CLICommand.setCurrent(command); + try { + runningThread.set(Thread.currentThread()); + int exit = command.main(connection.args.subList(1, connection.args.size()), connection.locale, connection.stdin, stdout, stderr); + stdout.flush(); + connection.sendExit(exit); + } finally { + CLICommand.setCurrent(orig); + runningThread.set(null); + } } } }; diff --git a/test/src/test/java/hudson/cli/CLITest.java b/test/src/test/java/hudson/cli/CLITest.java index f774de58e1..69ebcaa11e 100644 --- a/test/src/test/java/hudson/cli/CLITest.java +++ b/test/src/test/java/hudson/cli/CLITest.java @@ -120,9 +120,7 @@ public class CLITest { p.getBuildersList().add(new SleepBuilder(TimeUnit.MINUTES.toMillis(5))); doInterrupt(jar, p, "-remoting", "-i", privkey.getAbsolutePath()); doInterrupt(jar, p, "-ssh", "-user", "admin", "-i", privkey.getAbsolutePath()); - /* TODO does not yet work in HTTP mode: doInterrupt(jar, p, "-http", "-auth", "admin:admin"); - */ } private void doInterrupt(File jar, FreeStyleProject p, String... modeArgs) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); -- GitLab From a7bcf1fd0f94ff07604ff4ef00c4cce10f35241a Mon Sep 17 00:00:00 2001 From: Sathiya Narayanan Date: Mon, 3 Apr 2017 23:55:37 +0530 Subject: [PATCH 0075/2185] Fixed - Jenkins Global Configuration Save Option --- .../management/AdministrativeMonitorsConfiguration.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/jenkins/management/AdministrativeMonitorsConfiguration.java b/core/src/main/java/jenkins/management/AdministrativeMonitorsConfiguration.java index 390d5a3eec..d0dfa9abf5 100644 --- a/core/src/main/java/jenkins/management/AdministrativeMonitorsConfiguration.java +++ b/core/src/main/java/jenkins/management/AdministrativeMonitorsConfiguration.java @@ -27,6 +27,7 @@ package jenkins.management; import hudson.Extension; import hudson.model.AdministrativeMonitor; import jenkins.model.GlobalConfiguration; +import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -41,9 +42,15 @@ import java.util.logging.Logger; public class AdministrativeMonitorsConfiguration extends GlobalConfiguration { @Override public boolean configure(StaplerRequest req, JSONObject json) throws FormException { + JSONArray monitors = json.optJSONArray("administrativeMonitor"); for (AdministrativeMonitor am : AdministrativeMonitor.all()) { try { - boolean disable = !json.getJSONArray("administrativeMonitor").contains(am.id); + boolean disable; + if(monitors != null) { + disable = !monitors.contains(am.id); + }else { + disable = !am.id.equals(json.optString("administrativeMonitor")); + } am.disable(disable); } catch (IOException e) { LOGGER.log(Level.WARNING, "Failed to process form submission for " + am.id, e); -- GitLab From 56cee9518a2da6f01848eb08640c90ad07480c93 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 Apr 2017 11:11:40 -0400 Subject: [PATCH 0076/2185] Found another case where the Windows CI build was complaining about known_hosts metadata. --- test/src/test/java/hudson/cli/CLITest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/src/test/java/hudson/cli/CLITest.java b/test/src/test/java/hudson/cli/CLITest.java index 69ebcaa11e..ee4f7c4723 100644 --- a/test/src/test/java/hudson/cli/CLITest.java +++ b/test/src/test/java/hudson/cli/CLITest.java @@ -41,7 +41,8 @@ import jenkins.model.Jenkins; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.TeeOutputStream; -import static org.hamcrest.Matchers.containsString; +import org.apache.sshd.common.util.io.ModifiableFileWatcher; +import static org.hamcrest.Matchers.*; import org.jenkinsci.main.modules.cli.auth.ssh.UserPropertyImpl; import org.jenkinsci.main.modules.sshd.SSHD; import static org.junit.Assert.*; @@ -82,6 +83,8 @@ public class CLITest { } catch (IOException x) { assumeNoException("Sometimes on Windows KnownHostsServerKeyVerifier.acceptIncompleteHostKeys says WARNING: Failed (FileSystemException) to reload server keys from …\\\\.ssh\\\\known_hosts: … Incorrect function.", x); } + assumeThat("or on Windows DefaultKnownHostsServerKeyVerifier.reloadKnownHosts says invalid file permissions: Owner violation (Administrators)", + ModifiableFileWatcher.validateStrictConfigFilePermissions(known_hosts.toPath()), nullValue()); r.jenkins.setSecurityRealm(r.createDummySecurityRealm()); r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to("admin")); SSHD.get().setPort(0); -- GitLab From c74999f00c85f93104ff6fdea7d055e740b5eab9 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 Apr 2017 11:26:57 -0400 Subject: [PATCH 0077/2185] Found a race condition which could explain random CI hangs of CLIActionTest.authenticate. --- cli/src/main/java/hudson/cli/CLI.java | 15 +++++++++++---- core/src/main/java/hudson/cli/CLIAction.java | 17 ++++++++++++----- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/cli/src/main/java/hudson/cli/CLI.java b/cli/src/main/java/hudson/cli/CLI.java index d29ea675f4..bce0683679 100644 --- a/cli/src/main/java/hudson/cli/CLI.java +++ b/cli/src/main/java/hudson/cli/CLI.java @@ -712,6 +712,7 @@ public class CLI implements AutoCloseable { URL jenkins = new URL(url + "cli?remoting=false"); FullDuplexHttpStream streams = new FullDuplexHttpStream(jenkins, factory.authorization); class ClientSideImpl extends PlainCLIProtocol.ClientSide { + boolean complete; int exit = -1; ClientSideImpl(InputStream is, OutputStream os) throws IOException { super(is, os); @@ -720,9 +721,9 @@ public class CLI implements AutoCloseable { } } @Override - protected synchronized void onExit(int code) { + protected void onExit(int code) { this.exit = code; - notifyAll(); + finished(); } @Override protected void onStdout(byte[] chunk) throws IOException { @@ -733,7 +734,11 @@ public class CLI implements AutoCloseable { System.err.write(chunk); } @Override - protected synchronized void handleClose() { + protected void handleClose() { + finished(); + } + private synchronized void finished() { + complete = true; notifyAll(); } } @@ -761,7 +766,9 @@ public class CLI implements AutoCloseable { } }.start(); synchronized (connection) { - connection.wait(); + while (!connection.complete) { + connection.wait(); + } } return connection.exit; } diff --git a/core/src/main/java/hudson/cli/CLIAction.java b/core/src/main/java/hudson/cli/CLIAction.java index 15fefb75bb..402a287e95 100644 --- a/core/src/main/java/hudson/cli/CLIAction.java +++ b/core/src/main/java/hudson/cli/CLIAction.java @@ -135,6 +135,7 @@ public class CLIAction implements UnprotectedRootAction, StaplerProxy { protected void run(InputStream upload, OutputStream download) throws IOException, InterruptedException { final AtomicReference runningThread = new AtomicReference<>(); class ServerSideImpl extends PlainCLIProtocol.ServerSide { + boolean ready; List args = new ArrayList<>(); Locale locale = Locale.getDefault(); Charset encoding = Charset.defaultCharset(); @@ -167,8 +168,8 @@ public class CLIAction implements UnprotectedRootAction, StaplerProxy { } } @Override - protected synchronized void onStart() { - notifyAll(); + protected void onStart() { + ready(); } @Override protected void onStdin(byte[] chunk) throws IOException { @@ -179,18 +180,24 @@ public class CLIAction implements UnprotectedRootAction, StaplerProxy { stdinMatch.close(); } @Override - protected synchronized void handleClose() { - notifyAll(); + protected void handleClose() { + ready(); Thread t = runningThread.get(); if (t != null) { t.interrupt(); } } + private synchronized void ready() { + ready = true; + notifyAll(); + } } try (ServerSideImpl connection = new ServerSideImpl(upload, download)) { connection.begin(); synchronized (connection) { - connection.wait(); + while (!connection.ready) { + connection.wait(); + } } PrintStream stdout = new PrintStream(connection.streamStdout(), false, connection.encoding.name()); PrintStream stderr = new PrintStream(connection.streamStderr(), true, connection.encoding.name()); -- GitLab From 4c1910a10bddf5ac3d0c0e8253db5eb147a4b1be Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 Apr 2017 13:24:20 -0400 Subject: [PATCH 0078/2185] Silence an annoying message. --- cli/src/main/java/hudson/cli/CLI.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cli/src/main/java/hudson/cli/CLI.java b/cli/src/main/java/hudson/cli/CLI.java index bce0683679..9b245e0bd9 100644 --- a/cli/src/main/java/hudson/cli/CLI.java +++ b/cli/src/main/java/hudson/cli/CLI.java @@ -91,6 +91,7 @@ import org.apache.sshd.client.keyverifier.KnownHostsServerKeyVerifier; import org.apache.sshd.client.keyverifier.ServerKeyVerifier; import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.common.future.WaitableFuture; +import org.apache.sshd.common.util.SecurityUtils; import org.apache.sshd.common.util.io.NoCloseInputStream; import org.apache.sshd.common.util.io.NoCloseOutputStream; @@ -641,6 +642,7 @@ public class CLI implements AutoCloseable { } private static int sshConnection(String jenkinsUrl, String user, List args, PrivateKeyProvider provider, final boolean strictHostKey) throws IOException { + Logger.getLogger(SecurityUtils.class.getName()).setLevel(Level.WARNING); // suppress: BouncyCastle not registered, using the default JCE provider URL url = new URL(jenkinsUrl + "/login"); URLConnection conn = url.openConnection(); String endpointDescription = conn.getHeaderField("X-SSH-Endpoint"); -- GitLab From 03aa2a1f1778e8e847bbffe2bbcc97c33cc08fa4 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 Apr 2017 13:48:21 -0400 Subject: [PATCH 0079/2185] Simpler to catch ReadPendingException in just one place. --- cli/src/main/java/hudson/cli/PlainCLIProtocol.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/cli/src/main/java/hudson/cli/PlainCLIProtocol.java b/cli/src/main/java/hudson/cli/PlainCLIProtocol.java index db55ff105d..ba1f6733f2 100644 --- a/cli/src/main/java/hudson/cli/PlainCLIProtocol.java +++ b/cli/src/main/java/hudson/cli/PlainCLIProtocol.java @@ -105,12 +105,6 @@ class PlainCLIProtocol { } catch (EOFException x) { handleClose(); break; // TODO verify that we hit EOF immediately, not partway into framelen - } catch (ReadPendingException x) { - LOGGER.log(Level.FINE, null, x); - // TODO what does this signify? Seems to be thrown randomly by org.eclipse.jetty.io.FillInterest.register. No obvious impact. - // Check https://github.com/eclipse/jetty.project/issues/1047 in 9.4.3.v20170317 but cf. https://github.com/joakime/jetty-async-bug/issues/1 - handleClose(); - break; } if (framelen < 0) { throw new IOException("corrupt stream: negative frame length"); @@ -140,8 +134,10 @@ class PlainCLIProtocol { } } catch (IOException x) { LOGGER.log(Level.WARNING, null, flightRecorder.analyzeCrash(x, "broken stream")); - } catch (ReadPendingException x) { // as above + } catch (ReadPendingException x) { LOGGER.log(Level.FINE, null, x); + // TODO what does this signify? Seems to be thrown randomly by org.eclipse.jetty.io.FillInterest.register. No obvious impact. + // Check https://github.com/eclipse/jetty.project/issues/1047 in 9.4.3.v20170317 but cf. https://github.com/joakime/jetty-async-bug/issues/1 handleClose(); } } -- GitLab From fb311c687f9c1ac21cecf546d95843527f9a24ea Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 Apr 2017 14:01:57 -0400 Subject: [PATCH 0080/2185] Suppress a meaningless ClosedChannelException thrown by Jetty when killing a client. --- cli/src/main/java/hudson/cli/PlainCLIProtocol.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cli/src/main/java/hudson/cli/PlainCLIProtocol.java b/cli/src/main/java/hudson/cli/PlainCLIProtocol.java index ba1f6733f2..3156b4fa61 100644 --- a/cli/src/main/java/hudson/cli/PlainCLIProtocol.java +++ b/cli/src/main/java/hudson/cli/PlainCLIProtocol.java @@ -32,6 +32,7 @@ import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.channels.ClosedChannelException; import java.nio.channels.ReadPendingException; import java.util.logging.Level; import java.util.logging.Logger; @@ -132,6 +133,8 @@ class PlainCLIProtocol { IOUtils.skipFully(dis, framelen); } } + } catch (ClosedChannelException x) { + LOGGER.log(Level.FINE, null, x); } catch (IOException x) { LOGGER.log(Level.WARNING, null, flightRecorder.analyzeCrash(x, "broken stream")); } catch (ReadPendingException x) { -- GitLab From caf3e8e8436b5d0e433596bb71cac644aeb4d95a Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 Apr 2017 14:02:30 -0400 Subject: [PATCH 0081/2185] Removing comment corresponding to something I can no longer reproduce. --- core/src/main/java/jenkins/util/FullDuplexHttpService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/jenkins/util/FullDuplexHttpService.java b/core/src/main/java/jenkins/util/FullDuplexHttpService.java index db9106d7d1..534e2f5aab 100644 --- a/core/src/main/java/jenkins/util/FullDuplexHttpService.java +++ b/core/src/main/java/jenkins/util/FullDuplexHttpService.java @@ -127,7 +127,7 @@ public abstract class FullDuplexHttpService { // wait until we are done while (!completed) { - wait(); // TODO this can wait indefinitely even after the connection is broken + wait(); } } -- GitLab From 7f633dee58bbc2712990ff504f648cae86f63e43 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 Apr 2017 14:16:58 -0400 Subject: [PATCH 0082/2185] CLITest.interrupt could fail if you had a stale localhost entry in ~/.ssh/known_hosts. --- test/src/test/java/hudson/cli/CLITest.java | 23 ++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/test/src/test/java/hudson/cli/CLITest.java b/test/src/test/java/hudson/cli/CLITest.java index ee4f7c4723..daafba98e4 100644 --- a/test/src/test/java/hudson/cli/CLITest.java +++ b/test/src/test/java/hudson/cli/CLITest.java @@ -68,9 +68,8 @@ public class CLITest { @Rule public TemporaryFolder tmp = new TemporaryFolder(); - @Issue("JENKINS-41745") - @Test - public void strictHostKey() throws Exception { + /** Sets up a fake {@code user.home} so that tests {@code -ssh} mode does not get confused by the developer’s real {@code ~/.ssh/known_hosts}. */ + private File tempHome() throws IOException { File home = tmp.newFolder(); // Seems it gets created automatically but with inappropriate permissions: File known_hosts = new File(new File(home, ".ssh"), "known_hosts"); @@ -85,6 +84,13 @@ public class CLITest { } assumeThat("or on Windows DefaultKnownHostsServerKeyVerifier.reloadKnownHosts says invalid file permissions: Owner violation (Administrators)", ModifiableFileWatcher.validateStrictConfigFilePermissions(known_hosts.toPath()), nullValue()); + return home; + } + + @Issue("JENKINS-41745") + @Test + public void strictHostKey() throws Exception { + File home = tempHome(); r.jenkins.setSecurityRealm(r.createDummySecurityRealm()); r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to("admin")); SSHD.get().setPort(0); @@ -111,6 +117,7 @@ public class CLITest { @Issue("JENKINS-41745") @Test public void interrupt() throws Exception { + File home = tempHome(); r.jenkins.setSecurityRealm(r.createDummySecurityRealm()); r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to("admin")); SSHD.get().setPort(0); @@ -121,13 +128,13 @@ public class CLITest { User.get("admin").addProperty(new UserPropertyImpl(IOUtils.toString(CLITest.class.getResource("id_rsa.pub")))); FreeStyleProject p = r.createFreeStyleProject("p"); p.getBuildersList().add(new SleepBuilder(TimeUnit.MINUTES.toMillis(5))); - doInterrupt(jar, p, "-remoting", "-i", privkey.getAbsolutePath()); - doInterrupt(jar, p, "-ssh", "-user", "admin", "-i", privkey.getAbsolutePath()); - doInterrupt(jar, p, "-http", "-auth", "admin:admin"); + doInterrupt(jar, home, p, "-remoting", "-i", privkey.getAbsolutePath()); + doInterrupt(jar, home, p, "-ssh", "-user", "admin", "-i", privkey.getAbsolutePath()); + doInterrupt(jar, home, p, "-http", "-auth", "admin:admin"); } - private void doInterrupt(File jar, FreeStyleProject p, String... modeArgs) throws Exception { + private void doInterrupt(File jar, File home, FreeStyleProject p, String... modeArgs) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - List args = Lists.newArrayList("java", "-jar", jar.getAbsolutePath(), "-s", r.getURL().toString()); + List args = Lists.newArrayList("java", "-Duser.home=" + home, "-jar", jar.getAbsolutePath(), "-s", r.getURL().toString()); args.addAll(Arrays.asList(modeArgs)); args.addAll(Arrays.asList("build", "-s", "-v", "p")); Proc proc = new Launcher.LocalLauncher(StreamTaskListener.fromStderr()).launch().cmds(args).stdout(new TeeOutputStream(baos, System.out)).stderr(System.err).start(); -- GitLab From f7006b4674205ee4ad655deb8a6d42d34a5988bc Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 Apr 2017 14:46:20 -0400 Subject: [PATCH 0083/2185] Jetty 9.4.3.v20170317 refuses to set an empty HTTP header, leading to cryptic client errors since Hudson-Duplex is missing. --- core/src/main/java/jenkins/util/FullDuplexHttpService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/jenkins/util/FullDuplexHttpService.java b/core/src/main/java/jenkins/util/FullDuplexHttpService.java index 534e2f5aab..d82f37b30c 100644 --- a/core/src/main/java/jenkins/util/FullDuplexHttpService.java +++ b/core/src/main/java/jenkins/util/FullDuplexHttpService.java @@ -153,7 +153,7 @@ public abstract class FullDuplexHttpService { // the actual authentication for the connecting Channel is done by CLICommand UUID uuid = UUID.fromString(req.getHeader("Session")); - rsp.setHeader("Hudson-Duplex", ""); // set the header so that the client would know + rsp.setHeader("Hudson-Duplex", "true"); // set the header so that the client would know if (req.getHeader("Side").equals("download")) { FullDuplexHttpService service = createService(req, uuid); -- GitLab From 3b92399db8b71cbb28ef5fb78b763a9317ec9804 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 Apr 2017 14:50:46 -0400 Subject: [PATCH 0084/2185] =?UTF-8?q?Tested=20Jetty=209.4.3.v20170317=20bu?= =?UTF-8?q?t=20ReadPendingException=E2=80=99s=20did=20not=20disappear.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cli/src/main/java/hudson/cli/PlainCLIProtocol.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/hudson/cli/PlainCLIProtocol.java b/cli/src/main/java/hudson/cli/PlainCLIProtocol.java index 3156b4fa61..8cbfc94b4a 100644 --- a/cli/src/main/java/hudson/cli/PlainCLIProtocol.java +++ b/cli/src/main/java/hudson/cli/PlainCLIProtocol.java @@ -140,7 +140,7 @@ class PlainCLIProtocol { } catch (ReadPendingException x) { LOGGER.log(Level.FINE, null, x); // TODO what does this signify? Seems to be thrown randomly by org.eclipse.jetty.io.FillInterest.register. No obvious impact. - // Check https://github.com/eclipse/jetty.project/issues/1047 in 9.4.3.v20170317 but cf. https://github.com/joakime/jetty-async-bug/issues/1 + // Contrary to https://github.com/eclipse/jetty.project/issues/1047 this still happens in 9.4.3.v20170317 as in https://github.com/joakime/jetty-async-bug/issues/1 handleClose(); } } -- GitLab From 94d5f96d12d0a11ffebd207f45c19a83a0ce8436 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 Apr 2017 15:16:05 -0400 Subject: [PATCH 0085/2185] Figured out why Security232Test.commonsCollections1 was being skipped 10% of the time. --- test/src/test/java/jenkins/security/Security232Test.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/src/test/java/jenkins/security/Security232Test.java b/test/src/test/java/jenkins/security/Security232Test.java index f5a33ab6ab..65e0ea05d4 100644 --- a/test/src/test/java/jenkins/security/Security232Test.java +++ b/test/src/test/java/jenkins/security/Security232Test.java @@ -28,10 +28,10 @@ import java.rmi.activation.ActivationInstantiator; import java.rmi.server.ObjID; import java.rmi.server.RemoteObject; import java.rmi.server.UnicastRemoteObject; +import java.util.Collections; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.net.SocketFactory; -import static jenkins.security.security218.Payload.CommonsCollections1; import jenkins.security.security218.ysoserial.payloads.CommonsCollections1; import jenkins.security.security218.ysoserial.payloads.ObjectPayload; import static org.junit.Assert.*; @@ -59,6 +59,8 @@ public class Security232Test { @Test public void commonsCollections1() throws Exception { + r.jenkins.setAgentProtocols(Collections.singleton("CLI-connect")); // override CliProtocol.OPT_IN + File pwned = new File(r.jenkins.getRootDir(), "pwned"); int jrmpPort = 12345; -- GitLab From dbe24a85a39560b02d2b3c492ea0322dcf177d8b Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 Apr 2017 15:38:34 -0400 Subject: [PATCH 0086/2185] Using CLICommandInvoker in cases where we did not actually need to be testing Remoting-based CLI. --- .../test/java/hudson/cli/CLIActionTest.java | 2 +- .../java/hudson/model/ComputerSetTest.java | 23 +++++---------- .../model/listeners/ItemListenerTest.java | 29 ++++++------------- .../security/CliAuthenticationTest.java | 6 ++-- .../security/Security218BlackBoxTest.java | 2 +- .../jenkins/security/Security218CliTest.java | 3 +- 6 files changed, 23 insertions(+), 42 deletions(-) diff --git a/test/src/test/java/hudson/cli/CLIActionTest.java b/test/src/test/java/hudson/cli/CLIActionTest.java index 7333c3418b..6aa8478275 100644 --- a/test/src/test/java/hudson/cli/CLIActionTest.java +++ b/test/src/test/java/hudson/cli/CLIActionTest.java @@ -87,7 +87,7 @@ public class CLIActionTest { } - @SuppressWarnings({"unchecked", "rawtypes"}) // intentionally passing an unreifiable argument here + @SuppressWarnings({"unchecked", "rawtypes", "deprecation"}) // intentionally passing an unreifiable argument here; Remoting-based constructor intentional @Test public void security218_take2() throws Exception { pool = Executors.newCachedThreadPool(); diff --git a/test/src/test/java/hudson/model/ComputerSetTest.java b/test/src/test/java/hudson/model/ComputerSetTest.java index 56878bf4cd..38418e9cc0 100644 --- a/test/src/test/java/hudson/model/ComputerSetTest.java +++ b/test/src/test/java/hudson/model/ComputerSetTest.java @@ -23,18 +23,13 @@ */ package hudson.model; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertTrue; - -import hudson.cli.CLI; +import com.gargoylesoftware.htmlunit.html.HtmlForm; +import hudson.cli.CLICommandInvoker; import hudson.slaves.DumbSlave; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; import org.junit.Rule; import org.junit.Test; -import com.gargoylesoftware.htmlunit.html.HtmlForm; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.JenkinsRule.WebClient; @@ -69,14 +64,12 @@ public class ComputerSetTest { public void nodeOfflineCli() throws Exception { DumbSlave s = j.createSlave(); - try (CLI cli = new CLI(j.getURL())) { - assertTrue(cli.execute("wait-node-offline","xxx")!=0); - assertTrue(cli.execute("wait-node-online",s.getNodeName())==0); + assertThat(new CLICommandInvoker(j, "wait-node-offline").invokeWithArgs("xxx"), CLICommandInvoker.Matcher.failedWith(/* IllegalArgumentException from NodeOptionHandler */ 3)); + assertThat(new CLICommandInvoker(j, "wait-node-online").invokeWithArgs(s.getNodeName()), CLICommandInvoker.Matcher.succeededSilently()); - s.toComputer().disconnect().get(); + s.toComputer().disconnect(null).get(); - assertTrue(cli.execute("wait-node-offline",s.getNodeName())==0); - } + assertThat(new CLICommandInvoker(j, "wait-node-offline").invokeWithArgs(s.getNodeName()), CLICommandInvoker.Matcher.succeededSilently()); } @Test diff --git a/test/src/test/java/hudson/model/listeners/ItemListenerTest.java b/test/src/test/java/hudson/model/listeners/ItemListenerTest.java index bf19e6ea0d..85d3160154 100644 --- a/test/src/test/java/hudson/model/listeners/ItemListenerTest.java +++ b/test/src/test/java/hudson/model/listeners/ItemListenerTest.java @@ -23,21 +23,15 @@ */ package hudson.model.listeners; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import hudson.cli.CLI; +import hudson.cli.CLICommandInvoker; import hudson.model.Item; +import java.io.ByteArrayInputStream; +import static org.junit.Assert.*; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.jvnet.hudson.test.JenkinsRule; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.util.Arrays; - /** * Tests for ItemListener events. * @author Alan.Harder@sun.com @@ -64,16 +58,11 @@ public class ItemListenerTest { @Test public void onCreatedViaCLI() throws Exception { - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - PrintStream out = new PrintStream(buf); - try (CLI cli = new CLI(j.getURL())) { - cli.execute(Arrays.asList("create-job", "testJob"), - new ByteArrayInputStream(("" - + "").getBytes()), - out, out); - out.flush(); - assertNotNull("job should be created: " + buf, j.jenkins.getItem("testJob")); - assertEquals("onCreated event should be triggered: " + buf, "C", events.toString()); - } + CLICommandInvoker.Result result = new CLICommandInvoker(j, "create-job"). + withStdin(new ByteArrayInputStream(("").getBytes())). + invokeWithArgs("testJob"); + assertThat(result, CLICommandInvoker.Matcher.succeeded()); + assertNotNull("job should be created: " + result, j.jenkins.getItem("testJob")); + assertEquals("onCreated event should be triggered: " + result, "C", events.toString()); } } diff --git a/test/src/test/java/hudson/security/CliAuthenticationTest.java b/test/src/test/java/hudson/security/CliAuthenticationTest.java index 52b17fc9eb..bdddf14407 100644 --- a/test/src/test/java/hudson/security/CliAuthenticationTest.java +++ b/test/src/test/java/hudson/security/CliAuthenticationTest.java @@ -7,9 +7,6 @@ import static org.junit.Assert.assertTrue; import hudson.cli.CLI; import hudson.cli.CLICommand; -import hudson.cli.ClientAuthenticationCache; -import hudson.cli.LoginCommand; -import hudson.cli.LogoutCommand; import jenkins.model.Jenkins; import org.acegisecurity.Authentication; import org.apache.commons.io.input.NullInputStream; @@ -25,6 +22,7 @@ import java.util.Arrays; /** * @author Kohsuke Kawaguchi */ +@SuppressWarnings("deprecation") // Remoting-based CLI usages intentional public class CliAuthenticationTest { @Rule @@ -88,7 +86,7 @@ public class CliAuthenticationTest { } @Test - @For({LoginCommand.class, LogoutCommand.class, ClientAuthenticationCache.class}) + @For({hudson.cli.LoginCommand.class, hudson.cli.LogoutCommand.class, hudson.cli.ClientAuthenticationCache.class}) public void login() throws Exception { j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); diff --git a/test/src/test/java/jenkins/security/Security218BlackBoxTest.java b/test/src/test/java/jenkins/security/Security218BlackBoxTest.java index 4776ee92e4..05bd4069d7 100644 --- a/test/src/test/java/jenkins/security/Security218BlackBoxTest.java +++ b/test/src/test/java/jenkins/security/Security218BlackBoxTest.java @@ -85,7 +85,7 @@ public class Security218BlackBoxTest { @Rule public JenkinsRule r = new JenkinsRule(); - @SuppressWarnings("deprecation") // really mean to use getPage(String) + @SuppressWarnings("deprecation") // really mean to use getPage(String), and Remoting-based CLI @PresetData(PresetData.DataSet.ANONYMOUS_READONLY) // TODO userContent inaccessible without authentication otherwise @Test public void probe() throws Exception { diff --git a/test/src/test/java/jenkins/security/Security218CliTest.java b/test/src/test/java/jenkins/security/Security218CliTest.java index f9f39c4d46..cfa0002d71 100644 --- a/test/src/test/java/jenkins/security/Security218CliTest.java +++ b/test/src/test/java/jenkins/security/Security218CliTest.java @@ -41,6 +41,7 @@ import org.jvnet.hudson.test.TestExtension; import org.jvnet.hudson.test.recipes.PresetData; import org.kohsuke.args4j.Argument; +@SuppressWarnings("deprecation") // Remoting-based CLI usages intentional public class Security218CliTest { @Rule @@ -197,8 +198,8 @@ public class Security218CliTest { @Argument(metaVar = "command", usage = "Command to be launched by the payload", required = true, index = 1) public String command; - + @Override protected int run() throws Exception { Payload payloadItem = Payload.valueOf(this.payload); PayloadCaller callable = new PayloadCaller(payloadItem, command); -- GitLab From a87e10a5b9d4f832d47d063b9f123ddee39887f8 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 Apr 2017 17:15:30 -0400 Subject: [PATCH 0087/2185] Update node, npm, download-maven-plugin, and frontend-maven-plugin. --- war/pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/war/pom.xml b/war/pom.xml index fea88c9ede..765bbc9b8a 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -41,8 +41,8 @@ THE SOFTWARE. ${basedir}/work /jenkins 8080 - 4.0.0 - 3.8.9 + 6.10.2 + 3.10.10 @@ -642,7 +642,7 @@ THE SOFTWARE. com.googlecode.maven-download-plugin download-maven-plugin - 1.2.1 + 1.3.0 get-node @@ -706,7 +706,7 @@ THE SOFTWARE. com.github.eirslett frontend-maven-plugin - 1.0 + 1.4 -- GitLab From 5caee586974271813bb833591726a693469dfe1f Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 Apr 2017 19:05:37 -0400 Subject: [PATCH 0088/2185] Noting that ssh-cli-auth is obsolete. --- war/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/war/pom.xml b/war/pom.xml index af76805b69..a458abd41a 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -101,7 +101,7 @@ THE SOFTWARE. instance-identity 2.1 - + org.jenkins-ci.modules ssh-cli-auth 1.2 -- GitLab From 9fc59bf251a597d87d3f8e56069c74bed2605156 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 4 Apr 2017 19:15:03 -0400 Subject: [PATCH 0089/2185] =?UTF-8?q?Finally=20figured=20out=20how=20to=20?= =?UTF-8?q?suppress=20the=20ReadPendingException=20from=20Jetty.=20Somethi?= =?UTF-8?q?ng=C2=B9=20seems=20to=20interrupt=20the=20HTTP=20duplex=20servi?= =?UTF-8?q?ce=20thread,=20and=20Jetty=20apparently=20deals=20poorly=20with?= =?UTF-8?q?=20interruptions.=20So=20introduce=20an=20extra=20sleep=20to=20?= =?UTF-8?q?soak=20up=20the=20interrupt.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ¹Who? http://stackoverflow.com/questions/2126997/who-is-calling-the-java-thread-interrupt-method-if-im-not#comment40865453_2127397 --- cli/src/main/java/hudson/cli/PlainCLIProtocol.java | 4 ++-- core/src/main/java/hudson/cli/CLIAction.java | 5 +++++ test/src/test/java/hudson/cli/CLIActionTest.java | 6 ++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/cli/src/main/java/hudson/cli/PlainCLIProtocol.java b/cli/src/main/java/hudson/cli/PlainCLIProtocol.java index 8cbfc94b4a..8d1a080396 100644 --- a/cli/src/main/java/hudson/cli/PlainCLIProtocol.java +++ b/cli/src/main/java/hudson/cli/PlainCLIProtocol.java @@ -135,12 +135,12 @@ class PlainCLIProtocol { } } catch (ClosedChannelException x) { LOGGER.log(Level.FINE, null, x); + handleClose(); } catch (IOException x) { LOGGER.log(Level.WARNING, null, flightRecorder.analyzeCrash(x, "broken stream")); } catch (ReadPendingException x) { + // in case trick in CLIAction does not work LOGGER.log(Level.FINE, null, x); - // TODO what does this signify? Seems to be thrown randomly by org.eclipse.jetty.io.FillInterest.register. No obvious impact. - // Contrary to https://github.com/eclipse/jetty.project/issues/1047 this still happens in 9.4.3.v20170317 as in https://github.com/joakime/jetty-async-bug/issues/1 handleClose(); } } diff --git a/core/src/main/java/hudson/cli/CLIAction.java b/core/src/main/java/hudson/cli/CLIAction.java index 402a287e95..2c37db46cc 100644 --- a/core/src/main/java/hudson/cli/CLIAction.java +++ b/core/src/main/java/hudson/cli/CLIAction.java @@ -221,6 +221,11 @@ public class CLIAction implements UnprotectedRootAction, StaplerProxy { int exit = command.main(connection.args.subList(1, connection.args.size()), connection.locale, connection.stdin, stdout, stderr); stdout.flush(); connection.sendExit(exit); + try { // seems to avoid ReadPendingException from Jetty + Thread.sleep(1000); + } catch (InterruptedException x) { + // expected; ignore + } } finally { CLICommand.setCurrent(orig); runningThread.set(null); diff --git a/test/src/test/java/hudson/cli/CLIActionTest.java b/test/src/test/java/hudson/cli/CLIActionTest.java index 6aa8478275..4c7b2f570d 100644 --- a/test/src/test/java/hudson/cli/CLIActionTest.java +++ b/test/src/test/java/hudson/cli/CLIActionTest.java @@ -27,6 +27,7 @@ import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; import jenkins.model.Jenkins; import jenkins.security.ApiTokenProperty; import jenkins.util.Timer; @@ -39,6 +40,7 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.LoggerRule; import org.jvnet.hudson.test.MockAuthorizationStrategy; import org.jvnet.hudson.test.TestExtension; import org.jvnet.hudson.test.recipes.PresetData; @@ -54,6 +56,9 @@ public class CLIActionTest { @Rule public TemporaryFolder tmp = new TemporaryFolder(); + @Rule + public LoggerRule logging = new LoggerRule(); + private ExecutorService pool; /** @@ -131,6 +136,7 @@ public class CLIActionTest { @Issue({"JENKINS-12543", "JENKINS-41745"}) @Test public void authentication() throws Exception { + logging.record(PlainCLIProtocol.class, Level.FINE); File jar = tmp.newFile("jenkins-cli.jar"); FileUtils.copyURLToFile(j.jenkins.getJnlpJars("jenkins-cli.jar").getURL(), jar); j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); -- GitLab From 7bca0a1eefdee946eb8e39e06869aae8b616951f Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 5 Apr 2017 09:48:56 -0400 Subject: [PATCH 0090/2185] Refined implementation of -logger. --- cli/src/main/java/hudson/cli/CLI.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/src/main/java/hudson/cli/CLI.java b/cli/src/main/java/hudson/cli/CLI.java index 9b245e0bd9..e79cd15062 100644 --- a/cli/src/main/java/hudson/cli/CLI.java +++ b/cli/src/main/java/hudson/cli/CLI.java @@ -77,7 +77,7 @@ import java.util.Properties; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.logging.ConsoleHandler; +import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; import static java.util.logging.Level.*; @@ -544,11 +544,11 @@ public class CLI implements AutoCloseable { } if (head.equals("-logger") && args.size() >= 2) { Level level = parse(args.get(1)); - ConsoleHandler h = new ConsoleHandler(); - h.setLevel(level); + for (Handler h : Logger.getLogger("").getHandlers()) { + h.setLevel(level); + } for (Logger logger : new Logger[] {LOGGER, PlainCLIProtocol.LOGGER, Logger.getLogger("org.apache.sshd")}) { // perhaps also Channel logger.setLevel(level); - logger.addHandler(h); } args = args.subList(2, args.size()); continue; -- GitLab From 3939820c5f544a0ca5f73d8bf709e647f17a46e7 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 5 Apr 2017 09:53:14 -0400 Subject: [PATCH 0091/2185] Using logger as suggested by @rsandell. --- cli/src/main/java/hudson/cli/CLI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/hudson/cli/CLI.java b/cli/src/main/java/hudson/cli/CLI.java index e79cd15062..32053b0c17 100644 --- a/cli/src/main/java/hudson/cli/CLI.java +++ b/cli/src/main/java/hudson/cli/CLI.java @@ -763,7 +763,7 @@ public class CLI implements AutoCloseable { } connection.sendEndStdin(); } catch (IOException x) { - x.printStackTrace(); + LOGGER.log(Level.WARNING, null, x); } } }.start(); -- GitLab From da581599fd728c067b0d105d8c024829fac3e4ab Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 5 Apr 2017 18:28:37 -0400 Subject: [PATCH 0092/2185] -http mode broke when the user omitted the mandatory final `/` in the Jenkins URL. --- cli/src/main/java/hudson/cli/CLI.java | 32 +++++++++---------- cli/src/main/java/hudson/cli/CliPort.java | 6 ++-- .../java/hudson/cli/FullDuplexHttpStream.java | 24 +++++++++++--- .../test/java/hudson/cli/CLIActionTest.java | 7 ++-- test/src/test/java/hudson/cli/CLITest.java | 2 +- .../security/Security218BlackBoxTest.java | 2 +- 6 files changed, 44 insertions(+), 29 deletions(-) diff --git a/cli/src/main/java/hudson/cli/CLI.java b/cli/src/main/java/hudson/cli/CLI.java index 32053b0c17..fe00ce5378 100644 --- a/cli/src/main/java/hudson/cli/CLI.java +++ b/cli/src/main/java/hudson/cli/CLI.java @@ -146,19 +146,16 @@ public class CLI implements AutoCloseable { this.authorization = factory.authorization; ExecutorService exec = factory.exec; - String url = jenkins.toExternalForm(); - if(!url.endsWith("/")) url+='/'; - ownsPool = exec==null; pool = exec!=null ? exec : Executors.newCachedThreadPool(new NamingThreadFactory(Executors.defaultThreadFactory(), "CLI.pool")); Channel _channel; try { - _channel = connectViaCliPort(jenkins, getCliTcpPort(url)); + _channel = connectViaCliPort(jenkins, getCliTcpPort(jenkins)); } catch (IOException e) { LOGGER.log(Level.FINE, "Failed to connect via CLI port. Falling back to HTTP", e); try { - _channel = connectViaHttp(url); + _channel = connectViaHttp(jenkins); } catch (IOException e2) { e.addSuppressed(e2); throw e; @@ -177,12 +174,11 @@ public class CLI implements AutoCloseable { * @deprecated Specific to {@link Mode#REMOTING}. */ @Deprecated - private Channel connectViaHttp(String url) throws IOException { + private Channel connectViaHttp(URL url) throws IOException { LOGGER.log(FINE, "Trying to connect to {0} via Remoting over HTTP", url); - URL jenkins = new URL(url + "cli?remoting=true"); - FullDuplexHttpStream con = new FullDuplexHttpStream(jenkins,authorization); - Channel ch = new Channel("Chunked connection to "+jenkins, + FullDuplexHttpStream con = new FullDuplexHttpStream(url, "cli?remoting=true", authorization); + Channel ch = new Channel("Chunked connection to " + url, pool,con.getInputStream(),con.getOutputStream()); final long interval = 15*1000; final long timeout = (interval * 3) / 4; @@ -303,13 +299,14 @@ public class CLI implements AutoCloseable { /** * If the server advertises CLI endpoint, returns its location. + * @deprecated Specific to {@link Mode#REMOTING}. */ - protected CliPort getCliTcpPort(String url) throws IOException { - URL _url = new URL(url); - if (_url.getHost()==null || _url.getHost().length()==0) { + @Deprecated + protected CliPort getCliTcpPort(URL url) throws IOException { + if (url.getHost()==null || url.getHost().length()==0) { throw new IOException("Invalid URL: "+url); } - URLConnection head = _url.openConnection(); + URLConnection head = url.openConnection(); try { head.connect(); } catch (IOException e) { @@ -561,6 +558,10 @@ public class CLI implements AutoCloseable { return -1; } + if (!url.endsWith("/")) { + url += '/'; + } + if(args.isEmpty()) args = Arrays.asList("help"); // default to help @@ -643,7 +644,7 @@ public class CLI implements AutoCloseable { private static int sshConnection(String jenkinsUrl, String user, List args, PrivateKeyProvider provider, final boolean strictHostKey) throws IOException { Logger.getLogger(SecurityUtils.class.getName()).setLevel(Level.WARNING); // suppress: BouncyCastle not registered, using the default JCE provider - URL url = new URL(jenkinsUrl + "/login"); + URL url = new URL(jenkinsUrl + "login"); URLConnection conn = url.openConnection(); String endpointDescription = conn.getHeaderField("X-SSH-Endpoint"); @@ -711,8 +712,7 @@ public class CLI implements AutoCloseable { private static int plainHttpConnection(String url, List args, CLIConnectionFactory factory) throws IOException, InterruptedException { LOGGER.log(FINE, "Trying to connect to {0} via plain protocol over HTTP", url); - URL jenkins = new URL(url + "cli?remoting=false"); - FullDuplexHttpStream streams = new FullDuplexHttpStream(jenkins, factory.authorization); + FullDuplexHttpStream streams = new FullDuplexHttpStream(new URL(url), "cli?remoting=false", factory.authorization); class ClientSideImpl extends PlainCLIProtocol.ClientSide { boolean complete; int exit = -1; diff --git a/cli/src/main/java/hudson/cli/CliPort.java b/cli/src/main/java/hudson/cli/CliPort.java index 8faab7de41..56165e766b 100644 --- a/cli/src/main/java/hudson/cli/CliPort.java +++ b/cli/src/main/java/hudson/cli/CliPort.java @@ -8,9 +8,9 @@ import java.security.KeyFactory; import java.security.PublicKey; import java.security.spec.X509EncodedKeySpec; -/** - * @author Kohsuke Kawaguchi - */ + /** + * @deprecated Specific to Remoting mode. + */ public final class CliPort { /** * The TCP endpoint to talk to. diff --git a/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java b/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java index 3c7911e172..3d0192e630 100644 --- a/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java +++ b/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java @@ -20,7 +20,7 @@ import org.apache.commons.codec.binary.Base64; * @author Kohsuke Kawaguchi */ public class FullDuplexHttpStream { - private final URL target; + private final URL base; /** * Authorization header value needed to get through the HTTP layer. */ @@ -48,16 +48,31 @@ public class FullDuplexHttpStream { return null; } + @Deprecated + public FullDuplexHttpStream(URL target, String authorization) throws IOException { + this(new URL(target.toString().replaceFirst("/cli.*$", "")), target.toString().replaceFirst("(.+)/cli.*$", "$1"), authorization); + } + /** + * @param base the base URL of Jenkins * @param target * The endpoint that we are making requests to. * @param authorization * The value of the authorization header, if non-null. */ - public FullDuplexHttpStream(URL target, String authorization) throws IOException { - this.target = target; + public FullDuplexHttpStream(URL base, String relativeTarget, String authorization) throws IOException { + if (!base.toString().endsWith("/")) { + throw new IllegalArgumentException(base.toString()); + } + if (relativeTarget.startsWith("/")) { + throw new IllegalArgumentException(relativeTarget); + } + + this.base = base; this.authorization = authorization; + URL target = new URL(base, relativeTarget); + CrumbData crumbData = new CrumbData(); UUID uuid = UUID.randomUUID(); // so that the server can correlate those two connections @@ -128,8 +143,7 @@ public class FullDuplexHttpStream { } private String createCrumbUrlBase() { - String url = target.toExternalForm(); - return new StringBuilder(url.substring(0, url.lastIndexOf("/cli"))).append("/crumbIssuer/api/xml/").toString(); + return base + "crumbIssuer/api/xml/"; } private String readData(String dest) throws IOException { diff --git a/test/src/test/java/hudson/cli/CLIActionTest.java b/test/src/test/java/hudson/cli/CLIActionTest.java index 4c7b2f570d..b77e9baf8d 100644 --- a/test/src/test/java/hudson/cli/CLIActionTest.java +++ b/test/src/test/java/hudson/cli/CLIActionTest.java @@ -68,7 +68,7 @@ public class CLIActionTest { public void testDuplexHttp() throws Exception { pool = Executors.newCachedThreadPool(); try { - FullDuplexHttpStream con = new FullDuplexHttpStream(new URL(j.getURL(), "cli"), null); + FullDuplexHttpStream con = new FullDuplexHttpStream(j.getURL(), "cli", null); Channel ch = new ChannelBuilder("test connection", pool).build(con.getInputStream(), con.getOutputStream()); ch.close(); } finally { @@ -80,7 +80,7 @@ public class CLIActionTest { public void security218() throws Exception { pool = Executors.newCachedThreadPool(); try { - FullDuplexHttpStream con = new FullDuplexHttpStream(new URL(j.getURL(), "cli"), null); + FullDuplexHttpStream con = new FullDuplexHttpStream(j.getURL(), "cli", null); Channel ch = new ChannelBuilder("test connection", pool).build(con.getInputStream(), con.getOutputStream()); ch.call(new Security218()); fail("Expected the call to be rejected"); @@ -241,7 +241,8 @@ public class CLIActionTest { FileUtils.copyURLToFile(j.jenkins.getJnlpJars("jenkins-cli.jar").getURL(), jar); ByteArrayOutputStream baos = new ByteArrayOutputStream(); assertEquals(0, new Launcher.LocalLauncher(StreamTaskListener.fromStderr()).launch().cmds( - "java", "-Dfile.encoding=ISO-8859-2", "-Duser.language=cs", "-Duser.country=CZ", "-jar", jar.getAbsolutePath(), "-s", j.getURL().toString(),"-noKeyAuth", "test-diagnostic"). + "java", "-Dfile.encoding=ISO-8859-2", "-Duser.language=cs", "-Duser.country=CZ", "-jar", jar.getAbsolutePath(), + "-s", j.getURL().toString()./* just checking */replaceFirst("/$", ""), "-noKeyAuth", "test-diagnostic"). stdout(baos).stderr(System.err).join()); assertEquals("encoding=ISO-8859-2 locale=cs_CZ", baos.toString().trim()); // TODO test that stdout/stderr are in expected encoding (not true of -remoting mode!) diff --git a/test/src/test/java/hudson/cli/CLITest.java b/test/src/test/java/hudson/cli/CLITest.java index daafba98e4..db4613fa70 100644 --- a/test/src/test/java/hudson/cli/CLITest.java +++ b/test/src/test/java/hudson/cli/CLITest.java @@ -109,7 +109,7 @@ public class CLITest { assertThat(baos.toString(), containsString("Authenticated as: admin")); baos = new ByteArrayOutputStream(); assertEquals(0, new Launcher.LocalLauncher(StreamTaskListener.fromStderr()).launch().cmds( - "java", "-Duser.home=" + home, "-jar", jar.getAbsolutePath(), "-s", r.getURL().toString(), "-ssh", "-user", "admin", "-i", privkey.getAbsolutePath(), "-strictHostKey", "who-am-i" + "java", "-Duser.home=" + home, "-jar", jar.getAbsolutePath(), "-s", r.getURL().toString()./* just checking */replaceFirst("/$", ""), "-ssh", "-user", "admin", "-i", privkey.getAbsolutePath(), "-strictHostKey", "who-am-i" ).stdout(baos).stderr(System.err).join()); assertThat(baos.toString(), containsString("Authenticated as: admin")); } diff --git a/test/src/test/java/jenkins/security/Security218BlackBoxTest.java b/test/src/test/java/jenkins/security/Security218BlackBoxTest.java index 05bd4069d7..b420978b11 100644 --- a/test/src/test/java/jenkins/security/Security218BlackBoxTest.java +++ b/test/src/test/java/jenkins/security/Security218BlackBoxTest.java @@ -277,7 +277,7 @@ public class Security218BlackBoxTest { try { CLI cli = new CLI(r.getURL()) { @Override - protected CliPort getCliTcpPort(String url) throws IOException { + protected CliPort getCliTcpPort(URL url) throws IOException { return new CliPort(new InetSocketAddress(proxySocket.getInetAddress(), proxySocket.getLocalPort()), /* ignore identity */ null, 1); } }; -- GitLab From 724ebbcc5011917050468a58a470e655d6b1e598 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 5 Apr 2017 18:42:33 -0400 Subject: [PATCH 0093/2185] Improved error message displayed when using -http against a pre-JENKINS-41745 server. --- cli/src/main/java/hudson/cli/CLI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/hudson/cli/CLI.java b/cli/src/main/java/hudson/cli/CLI.java index fe00ce5378..2f6e15e0f4 100644 --- a/cli/src/main/java/hudson/cli/CLI.java +++ b/cli/src/main/java/hudson/cli/CLI.java @@ -719,7 +719,7 @@ public class CLI implements AutoCloseable { ClientSideImpl(InputStream is, OutputStream os) throws IOException { super(is, os); if (is.read() != 0) { // cf. FullDuplexHttpService - throw new IOException("expected to see initial zero byte"); + throw new IOException("expected to see initial zero byte; perhaps you are connecting to an old server which does not support -http?"); } } @Override -- GitLab From c41c97b3a2ab3dd3417f888e2b975182bc871780 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 6 Apr 2017 11:40:19 -0400 Subject: [PATCH 0094/2185] @stephenc requested documentation of the deprecated FullDuplexHttpStream constructor, and as it turns out it did not work. --- .../main/java/hudson/cli/FullDuplexHttpStream.java | 13 ++++++++++--- test/src/test/java/hudson/cli/CLIActionTest.java | 3 ++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java b/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java index 3d0192e630..c01f3261ce 100644 --- a/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java +++ b/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java @@ -48,9 +48,15 @@ public class FullDuplexHttpStream { return null; } + /** + * @param target something like {@code http://jenkins/cli?remoting=true} + * which we then need to split into {@code http://jenkins/} + {@code cli?remoting=true} + * in order to construct a crumb issuer request + * @deprecated use {@link #FullDuplexHttpStream(URL, String, String)} instead + */ @Deprecated public FullDuplexHttpStream(URL target, String authorization) throws IOException { - this(new URL(target.toString().replaceFirst("/cli.*$", "")), target.toString().replaceFirst("(.+)/cli.*$", "$1"), authorization); + this(new URL(target.toString().replaceFirst("/cli.*$", "/")), target.toString().replaceFirst("^.+/(cli.*)$", "$1"), authorization); } /** @@ -92,8 +98,9 @@ public class FullDuplexHttpStream { con.getOutputStream().close(); input = con.getInputStream(); // make sure we hit the right URL - if(con.getHeaderField("Hudson-Duplex")==null) - throw new IOException(target+" doesn't look like Jenkins"); + if (con.getHeaderField("Hudson-Duplex") == null) { + throw new IOException(target + " does not look like Jenkins, or is not serving the HTTP Duplex transport"); + } // client->server uses chunked encoded POST for unlimited capacity. con = (HttpURLConnection) target.openConnection(); diff --git a/test/src/test/java/hudson/cli/CLIActionTest.java b/test/src/test/java/hudson/cli/CLIActionTest.java index b77e9baf8d..8100663577 100644 --- a/test/src/test/java/hudson/cli/CLIActionTest.java +++ b/test/src/test/java/hudson/cli/CLIActionTest.java @@ -68,7 +68,8 @@ public class CLIActionTest { public void testDuplexHttp() throws Exception { pool = Executors.newCachedThreadPool(); try { - FullDuplexHttpStream con = new FullDuplexHttpStream(j.getURL(), "cli", null); + @SuppressWarnings("deprecation") // to verify compatibility of original constructor + FullDuplexHttpStream con = new FullDuplexHttpStream(new URL(j.getURL(), "cli"), null); Channel ch = new ChannelBuilder("test connection", pool).build(con.getInputStream(), con.getOutputStream()); ch.close(); } finally { -- GitLab From 6c63890e41a336961c5c29d77489853f0c08eaaf Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 6 Apr 2017 15:18:07 -0400 Subject: [PATCH 0095/2185] Demonstrating that interleaved stdio does work in -http mode. --- .../test/java/hudson/cli/CLIActionTest.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/src/test/java/hudson/cli/CLIActionTest.java b/test/src/test/java/hudson/cli/CLIActionTest.java index 8100663577..73fbbfb84c 100644 --- a/test/src/test/java/hudson/cli/CLIActionTest.java +++ b/test/src/test/java/hudson/cli/CLIActionTest.java @@ -17,6 +17,9 @@ import hudson.util.StreamTaskListener; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.io.PrintWriter; import java.lang.reflect.Field; import java.net.HttpURLConnection; import java.net.URL; @@ -32,6 +35,7 @@ import jenkins.model.Jenkins; import jenkins.security.ApiTokenProperty; import jenkins.util.Timer; import org.apache.commons.io.FileUtils; +import org.apache.commons.io.output.TeeOutputStream; import org.codehaus.groovy.runtime.Security218; import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.*; @@ -250,6 +254,33 @@ public class CLIActionTest { // -ssh mode does not pass client locale or encoding } + @Issue("JENKINS-41745") + @Test + public void interleavedStdio() throws Exception { + File jar = tmp.newFile("jenkins-cli.jar"); + FileUtils.copyURLToFile(j.jenkins.getJnlpJars("jenkins-cli.jar").getURL(), jar); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PipedInputStream pis = new PipedInputStream(); + PipedOutputStream pos = new PipedOutputStream(pis); + PrintWriter pw = new PrintWriter(new TeeOutputStream(pos, System.err), true); + Proc proc = new Launcher.LocalLauncher(StreamTaskListener.fromStderr()).launch().cmds( + "java", "-jar", jar.getAbsolutePath(), "-s", j.getURL().toString(), "-noKeyAuth", "groovysh"). + stdout(new TeeOutputStream(baos, System.out)).stderr(System.err).stdin(pis).start(); + while (!baos.toString().contains("000")) { // cannot just search for, say, "groovy:000> " since there are ANSI escapes there (cf. StringEscapeUtils.escapeJava) + Thread.sleep(100); + } + pw.println("11 * 11"); + while (!baos.toString().contains("121")) { // ditto not "===> 121" + Thread.sleep(100); + } + pw.println("11 * 11 * 11"); + while (!baos.toString().contains("1331")) { + Thread.sleep(100); + } + pw.println(":q"); + assertEquals(0, proc.join()); + } + @TestExtension("encodingAndLocale") public static class TestDiagnosticCommand extends CLICommand { -- GitLab From 9150d5b563c62be3ca8143f5ee7af8d5a063b79d Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 6 Apr 2017 15:30:53 -0400 Subject: [PATCH 0096/2185] Suppress an idle timeout from Jetty which would otherwise interfere with interactive commands. --- cli/src/main/java/hudson/cli/PlainCLIProtocol.java | 8 ++++++++ test/src/test/java/hudson/cli/CLIActionTest.java | 2 ++ 2 files changed, 10 insertions(+) diff --git a/cli/src/main/java/hudson/cli/PlainCLIProtocol.java b/cli/src/main/java/hudson/cli/PlainCLIProtocol.java index 8d1a080396..d27669a0c9 100644 --- a/cli/src/main/java/hudson/cli/PlainCLIProtocol.java +++ b/cli/src/main/java/hudson/cli/PlainCLIProtocol.java @@ -34,6 +34,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.channels.ClosedChannelException; import java.nio.channels.ReadPendingException; +import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.io.IOUtils; @@ -106,6 +107,13 @@ class PlainCLIProtocol { } catch (EOFException x) { handleClose(); break; // TODO verify that we hit EOF immediately, not partway into framelen + } catch (IOException x) { + if (x.getCause() instanceof TimeoutException) { + LOGGER.log(Level.FINE, "ignoring idle timeout, perhaps from Jetty", x); + continue; + } else { + throw x; + } } if (framelen < 0) { throw new IOException("corrupt stream: negative frame length"); diff --git a/test/src/test/java/hudson/cli/CLIActionTest.java b/test/src/test/java/hudson/cli/CLIActionTest.java index 73fbbfb84c..62ab20ce38 100644 --- a/test/src/test/java/hudson/cli/CLIActionTest.java +++ b/test/src/test/java/hudson/cli/CLIActionTest.java @@ -257,6 +257,7 @@ public class CLIActionTest { @Issue("JENKINS-41745") @Test public void interleavedStdio() throws Exception { + logging.record(PlainCLIProtocol.class, Level.FINE); File jar = tmp.newFile("jenkins-cli.jar"); FileUtils.copyURLToFile(j.jenkins.getJnlpJars("jenkins-cli.jar").getURL(), jar); ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -273,6 +274,7 @@ public class CLIActionTest { while (!baos.toString().contains("121")) { // ditto not "===> 121" Thread.sleep(100); } + Thread.sleep(31_000); // aggravate org.eclipse.jetty.io.IdleTimeout (cf. AbstractConnector._idleTimeout) pw.println("11 * 11 * 11"); while (!baos.toString().contains("1331")) { Thread.sleep(100); -- GitLab From 6ffc57ae79cabcd4083517fb66d2af91dd8fad12 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 6 Apr 2017 15:58:23 -0400 Subject: [PATCH 0097/2185] Verifying that PlainCLIProtocol ignores unrecognized opcodes, so long as it has received well-formed frames. --- .../java/hudson/cli/PlainCLIProtocolTest.java | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 cli/src/test/java/hudson/cli/PlainCLIProtocolTest.java diff --git a/cli/src/test/java/hudson/cli/PlainCLIProtocolTest.java b/cli/src/test/java/hudson/cli/PlainCLIProtocolTest.java new file mode 100644 index 0000000000..4663fbafd5 --- /dev/null +++ b/cli/src/test/java/hudson/cli/PlainCLIProtocolTest.java @@ -0,0 +1,132 @@ +/* + * The MIT License + * + * Copyright 2017 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 hudson.cli; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import static org.junit.Assert.*; +import org.junit.Test; + +public class PlainCLIProtocolTest { + + @Test + public void ignoreUnknownOperations() throws Exception { + final PipedOutputStream upload = new PipedOutputStream(); + final PipedOutputStream download = new PipedOutputStream(); + class Client extends PlainCLIProtocol.ClientSide { + int code = -1; + final ByteArrayOutputStream stdout = new ByteArrayOutputStream(); + Client() throws IOException { + super(new PipedInputStream(download), upload); + } + @Override + protected synchronized void onExit(int code) { + this.code = code; + notifyAll(); + } + @Override + protected void onStdout(byte[] chunk) throws IOException { + stdout.write(chunk); + } + @Override + protected void onStderr(byte[] chunk) throws IOException {} + @Override + protected void handleClose() {} + void send() throws IOException { + sendArg("command"); + sendStart(); + streamStdin().write("hello".getBytes()); + } + void newop() throws IOException { + dos.writeInt(0); + dos.writeByte(99); + dos.flush(); + } + } + class Server extends PlainCLIProtocol.ServerSide { + String arg; + boolean started; + final ByteArrayOutputStream stdin = new ByteArrayOutputStream(); + Server() throws IOException { + super(new PipedInputStream(upload), download); + } + @Override + protected void onArg(String text) { + arg = text; + } + @Override + protected void onLocale(String text) {} + @Override + protected void onEncoding(String text) {} + @Override + protected synchronized void onStart() { + started = true; + notifyAll(); + } + @Override + protected void onStdin(byte[] chunk) throws IOException { + stdin.write(chunk); + } + @Override + protected void onEndStdin() throws IOException {} + @Override + protected void handleClose() {} + void send() throws IOException { + streamStdout().write("goodbye".getBytes()); + sendExit(2); + } + void newop() throws IOException { + dos.writeInt(0); + dos.writeByte(99); + dos.flush(); + } + } + Client client = new Client(); + Server server = new Server(); + client.begin(); + server.begin(); + client.send(); + client.newop(); + synchronized (server) { + while (!server.started) { + server.wait(); + } + } + server.newop(); + server.send(); + synchronized (client) { + while (client.code == -1) { + client.wait(); + } + } + assertEquals("hello", server.stdin.toString()); + assertEquals("command", server.arg); + assertEquals("goodbye", client.stdout.toString()); + assertEquals(2, client.code); + } + +} -- GitLab From b831acd9854b525d680ca72fd218c848121b9d3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Zaj=C4=85czkowski?= Date: Fri, 7 Apr 2017 10:40:12 +0200 Subject: [PATCH 0098/2185] [JENKINS-42645] Case insensitive search by default for new and anonymous users (#2801) * [JENKINS-42645] Case insensitive search by default * [JENKINS-42960] Search in FixedSet more locale friendly String.equalsIgnoreCase is safer than toLowerCase when non English locales are used. * [JENKINS-42645] Review remarks --- .../src/main/java/hudson/search/FixedSet.java | 21 ++++---- .../hudson/search/UserSearchProperty.java | 15 +++--- core/src/test/java/hudson/model/ViewTest.java | 3 +- .../widgets/HistoryPageFilterTest.java | 31 ++++++------ ...oryPageFilterCaseSensitiveSearchTest.java} | 48 ++++++++++++++----- 5 files changed, 70 insertions(+), 48 deletions(-) rename test/src/test/java/jenkins/widgets/{HistoryPageFilterInsensitiveSearchTest.java => HistoryPageFilterCaseSensitiveSearchTest.java} (52%) diff --git a/core/src/main/java/hudson/search/FixedSet.java b/core/src/main/java/hudson/search/FixedSet.java index d00990dc39..f9ab1ade29 100644 --- a/core/src/main/java/hudson/search/FixedSet.java +++ b/core/src/main/java/hudson/search/FixedSet.java @@ -27,12 +27,15 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import org.apache.commons.lang.StringUtils; + /** * Set of {@link SearchItem}s that are statically known upfront. * * @author Kohsuke Kawaguchi */ public class FixedSet implements SearchIndex { + private final Collection items; public FixedSet(Collection items) { @@ -45,27 +48,21 @@ public class FixedSet implements SearchIndex { public void find(String token, List result) { boolean caseInsensitive = UserSearchProperty.isCaseInsensitive(); - for (SearchItem i : items){ + for (SearchItem i : items) { String name = i.getSearchName(); - if(caseInsensitive){ - token=token.toLowerCase(); - name=name.toLowerCase(); - } - if(token.equals(i.getSearchName())) + if (name.equals(token) || (caseInsensitive && name.equalsIgnoreCase(token))) { result.add(i); + } } } public void suggest(String token, List result) { boolean caseInsensitive = UserSearchProperty.isCaseInsensitive(); - for (SearchItem i : items){ + for (SearchItem i : items) { String name = i.getSearchName(); - if(caseInsensitive){ - token=token.toLowerCase(); - name=name.toLowerCase(); - } - if(name.contains(token)) + if (name.contains(token) || (caseInsensitive && StringUtils.containsIgnoreCase(name, token))) { result.add(i); + } } } } diff --git a/core/src/main/java/hudson/search/UserSearchProperty.java b/core/src/main/java/hudson/search/UserSearchProperty.java index 3d74013d3b..1a5438227c 100644 --- a/core/src/main/java/hudson/search/UserSearchProperty.java +++ b/core/src/main/java/hudson/search/UserSearchProperty.java @@ -11,7 +11,9 @@ import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.export.Exported; public class UserSearchProperty extends hudson.model.UserProperty { - + + private static final boolean DEFAULT_SEARCH_CASE_INSENSITIVE_MODE = true; + private final boolean insensitiveSearch; public UserSearchProperty(boolean insensitiveSearch) { @@ -25,11 +27,12 @@ public class UserSearchProperty extends hudson.model.UserProperty { public static boolean isCaseInsensitive(){ User user = User.current(); - boolean caseInsensitive = false; - if(user!=null && user.getProperty(UserSearchProperty.class).getInsensitiveSearch()){//Searching for anonymous user is case-sensitive - caseInsensitive=true; + + if (user == null) { + return DEFAULT_SEARCH_CASE_INSENSITIVE_MODE; } - return caseInsensitive; + + return user.getProperty(UserSearchProperty.class).getInsensitiveSearch(); } @@ -40,7 +43,7 @@ public class UserSearchProperty extends hudson.model.UserProperty { } public UserProperty newInstance(User user) { - return new UserSearchProperty(false); //default setting is case-sensitive searching + return new UserSearchProperty(DEFAULT_SEARCH_CASE_INSENSITIVE_MODE); } @Override diff --git a/core/src/test/java/hudson/model/ViewTest.java b/core/src/test/java/hudson/model/ViewTest.java index adcb7deee1..59a676291f 100644 --- a/core/src/test/java/hudson/model/ViewTest.java +++ b/core/src/test/java/hudson/model/ViewTest.java @@ -13,6 +13,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import javax.servlet.ServletException; @@ -98,7 +99,7 @@ public class ViewTest { final TopLevelItem rightJob = createJob("rightJob"); Mockito.when(leftView.getItems()).thenReturn(Arrays.asList(leftJob, sharedJob)); - Mockito.when(rightView.getItems()).thenReturn(Arrays.asList(rightJob)); + Mockito.when(rightView.getItems()).thenReturn(Collections.singletonList(rightJob)); final TopLevelItem[] expected = new TopLevelItem[] {rootJob, sharedJob, leftJob, rightJob}; diff --git a/core/src/test/java/jenkins/widgets/HistoryPageFilterTest.java b/core/src/test/java/jenkins/widgets/HistoryPageFilterTest.java index 9649264d0a..97db267f71 100644 --- a/core/src/test/java/jenkins/widgets/HistoryPageFilterTest.java +++ b/core/src/test/java/jenkins/widgets/HistoryPageFilterTest.java @@ -37,8 +37,8 @@ import hudson.model.Run; import hudson.model.StringParameterValue; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; +import org.jvnet.hudson.test.Issue; import org.mockito.Mockito; import java.io.IOException; @@ -326,24 +326,21 @@ public class HistoryPageFilterTest { } @Test - public void test_search_should_be_case_sensitive_for_anonymous_user() throws IOException { - //given - HistoryPageFilter historyPageFilter = newPage(5, null, null); - //and - historyPageFilter.setSearchString("failure"); - //and + @Issue("JENKINS-42645") + public void should_be_case_insensitive_by_default() throws IOException { List runs = Lists.newArrayList(new MockRun(2, Result.FAILURE), new MockRun(1, Result.SUCCESS)); - List queueItems = newQueueItems(3, 4); - - //when - historyPageFilter.add(runs, queueItems); + assertOneMatchingBuildForGivenSearchStringAndRunItems("failure", runs); + } - //then - Assert.assertEquals(0, historyPageFilter.runs.size()); + @Test + public void should_lower_case_search_string_in_case_insensitive_search() throws IOException { + List runs = Lists.newArrayList(new MockRun(2, Result.FAILURE), new MockRun(1, Result.SUCCESS)); + assertOneMatchingBuildForGivenSearchStringAndRunItems("FAILure", runs); } @Test - public void test_search_builds_by_build_variables() throws IOException { + @Issue("JENKINS-40718") + public void should_search_builds_by_build_variables() throws IOException { List runs = ImmutableList.of( new MockBuild(2).withBuildVariables(ImmutableMap.of("env", "dummyEnv")), new MockBuild(1).withBuildVariables(ImmutableMap.of("env", "otherEnv"))); @@ -351,7 +348,8 @@ public class HistoryPageFilterTest { } @Test - public void test_search_builds_by_build_params() throws IOException { + @Issue("JENKINS-40718") + public void should_search_builds_by_build_params() throws IOException { List runs = ImmutableList.of( new MockBuild(2).withBuildParameters(ImmutableMap.of("env", "dummyEnv")), new MockBuild(1).withBuildParameters(ImmutableMap.of("env", "otherEnv"))); @@ -359,7 +357,8 @@ public class HistoryPageFilterTest { } @Test - public void test_ignore_sensitive_parameters_in_search_builds_by_build_params() throws IOException { + @Issue("JENKINS-40718") + public void should_ignore_sensitive_parameters_in_search_builds_by_build_params() throws IOException { List runs = ImmutableList.of( new MockBuild(2).withBuildParameters(ImmutableMap.of("plainPassword", "pass1plain")), new MockBuild(1).withSensitiveBuildParameters("password", "pass1")); diff --git a/test/src/test/java/jenkins/widgets/HistoryPageFilterInsensitiveSearchTest.java b/test/src/test/java/jenkins/widgets/HistoryPageFilterCaseSensitiveSearchTest.java similarity index 52% rename from test/src/test/java/jenkins/widgets/HistoryPageFilterInsensitiveSearchTest.java rename to test/src/test/java/jenkins/widgets/HistoryPageFilterCaseSensitiveSearchTest.java index 41e821b695..e6d0f65085 100644 --- a/test/src/test/java/jenkins/widgets/HistoryPageFilterInsensitiveSearchTest.java +++ b/test/src/test/java/jenkins/widgets/HistoryPageFilterCaseSensitiveSearchTest.java @@ -8,6 +8,7 @@ import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; +import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; import org.mockito.Mockito; @@ -25,8 +26,11 @@ import hudson.security.AuthorizationStrategy; /** * TODO: Code partially duplicated with HistoryPageFilterTest in core + * + * Search in case insensitive mode is tested by unit tests in HistoryPageFilterTest. */ -public class HistoryPageFilterInsensitiveSearchTest { +@Issue({"JENKINS-40718", "JENKINS-42645"}) +public class HistoryPageFilterCaseSensitiveSearchTest { private static final String TEST_USER_NAME = "testUser"; @@ -34,31 +38,45 @@ public class HistoryPageFilterInsensitiveSearchTest { public JenkinsRule j = new JenkinsRule(); @Test - public void should_search_insensitively_when_enabled_for_user() throws IOException { - setUserContextAndAssertCaseInsensitivitySearchForGivenSearchString("failure"); + public void should_search_case_sensitively_when_enabled_for_user() throws IOException { + setCaseSensitiveSearchForUserAndCheckAssertionForGivenSearchString("FAILURE", new SearchResultAssertFunction() { + @Override + public void doAssertion(HistoryPageFilter historyPageFilter) { + Assert.assertEquals(1, historyPageFilter.runs.size()); + Assert.assertEquals(HistoryPageEntry.getEntryId(2), historyPageFilter.runs.get(0).getEntryId()); + } + }); } @Test - public void should_also_lower_search_query_in_insensitive_search_enabled() throws IOException { - setUserContextAndAssertCaseInsensitivitySearchForGivenSearchString("FAILure"); + public void should_skip_result_with_different_capitalization_when_case_sensitively_search_is_enabled_for_user() throws IOException { + setCaseSensitiveSearchForUserAndCheckAssertionForGivenSearchString("failure", new SearchResultAssertFunction() { + @Override + public void doAssertion(HistoryPageFilter historyPageFilter) { + Assert.assertEquals(0, historyPageFilter.runs.size()); + } + }); } - private void setUserContextAndAssertCaseInsensitivitySearchForGivenSearchString(final String searchString) throws IOException { + private void setCaseSensitiveSearchForUserAndCheckAssertionForGivenSearchString(final String searchString, + SearchResultAssertFunction assertionOnSearchResults) throws IOException { AuthorizationStrategy.Unsecured strategy = new AuthorizationStrategy.Unsecured(); j.jenkins.setAuthorizationStrategy(strategy); j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); UsernamePasswordAuthenticationToken testUserAuthentication = new UsernamePasswordAuthenticationToken(TEST_USER_NAME, "any"); - try (ACLContext acl = ACL.as(testUserAuthentication)) { - User.get(TEST_USER_NAME).addProperty(new UserSearchProperty(true)); + try (ACLContext ignored = ACL.as(testUserAuthentication)) { + User.get(TEST_USER_NAME).addProperty(new UserSearchProperty(false)); //test logic - List runs = ImmutableList.of(new MockRun(2, Result.FAILURE), new MockRun(1, Result.SUCCESS)); - assertOneMatchingBuildForGivenSearchStringAndRunItems(searchString, runs); + final List runs = ImmutableList.of(new MockRun(2, Result.FAILURE), new MockRun(1, Result.SUCCESS)); + assertNoMatchingBuildsForGivenSearchStringAndRunItems(searchString, runs, assertionOnSearchResults); } + } - private void assertOneMatchingBuildForGivenSearchStringAndRunItems(String searchString, List runs) { + private void assertNoMatchingBuildsForGivenSearchStringAndRunItems(String searchString, List runs, + SearchResultAssertFunction assertionOnSearchResults) { //given HistoryPageFilter historyPageFilter = new HistoryPageFilter<>(5); //and @@ -68,8 +86,7 @@ public class HistoryPageFilterInsensitiveSearchTest { historyPageFilter.add(runs, Collections.emptyList()); //then - Assert.assertEquals(1, historyPageFilter.runs.size()); - Assert.assertEquals(HistoryPageEntry.getEntryId(2), historyPageFilter.runs.get(0).getEntryId()); + assertionOnSearchResults.doAssertion(historyPageFilter); } @SuppressWarnings("unchecked") @@ -111,4 +128,9 @@ public class HistoryPageFilterInsensitiveSearchTest { return (int) queueId; } } + + //Waiting for Java 8... - coming soon - April 2017? + private interface SearchResultAssertFunction { + void doAssertion(HistoryPageFilter historyPageFilter); + } } -- GitLab From d35dfcb24fb2272076f863780fdc3de93d0ec04b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20Gond=C5=BEa?= Date: Thu, 6 Apr 2017 14:30:50 +0200 Subject: [PATCH 0099/2185] [FIXED JENKINS-37616] Make sure Cloud.PROVISION is properly initialized --- core/src/main/java/hudson/model/Computer.java | 6 ++++++ .../src/test/java/hudson/slaves/CloudTest.java | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) rename {core => test}/src/test/java/hudson/slaves/CloudTest.java (60%) diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index 3b87e42f23..17b4989089 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -28,6 +28,7 @@ package hudson.model; import hudson.EnvVars; import hudson.Extension; import hudson.Launcher.ProcStarter; +import hudson.slaves.Cloud; import jenkins.util.SystemProperties; import hudson.Util; import hudson.cli.declarative.CLIMethod; @@ -1720,5 +1721,10 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces public static final Permission CONNECT = new Permission(PERMISSIONS,"Connect", Messages._Computer_ConnectPermission_Description(), DISCONNECT, PermissionScope.COMPUTER); public static final Permission BUILD = new Permission(PERMISSIONS, "Build", Messages._Computer_BuildPermission_Description(), Permission.WRITE, PermissionScope.COMPUTER); + // This permission was historically scoped to this class albeit declared in Cloud. While deserializing, Jenkins loads + // the scope class to make sure the permission is initialized and registered. since Cloud class is used rather seldom, + // it might appear the permission does not exist. Referencing the permission from here to make sure it gets loaded. + private static final @Deprecated Permission CLOUD_PROVISION = Cloud.PROVISION; + private static final Logger LOGGER = Logger.getLogger(Computer.class.getName()); } diff --git a/core/src/test/java/hudson/slaves/CloudTest.java b/test/src/test/java/hudson/slaves/CloudTest.java similarity index 60% rename from core/src/test/java/hudson/slaves/CloudTest.java rename to test/src/test/java/hudson/slaves/CloudTest.java index bed5bad5f7..8b7c93bf6d 100644 --- a/core/src/test/java/hudson/slaves/CloudTest.java +++ b/test/src/test/java/hudson/slaves/CloudTest.java @@ -7,11 +7,17 @@ import hudson.security.Permission; import hudson.security.SidACL; import jenkins.model.Jenkins; import org.acegisecurity.acls.sid.Sid; +import org.junit.Rule; import org.junit.Test; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.WithoutJenkins; public class CloudTest { - @Test + @Rule public JenkinsRule j = new JenkinsRule(); + + @Test @WithoutJenkins @Issue("JENKINS-37616") public void provisionPermissionShouldBeIndependentFromAdminister() throws Exception { SidACL acl = new SidACL() { @Override protected Boolean hasPermission(Sid p, Permission permission) { @@ -23,4 +29,11 @@ public class CloudTest { assertFalse(acl.hasPermission(Jenkins.ANONYMOUS, Jenkins.ADMINISTER)); assertEquals(Cloud.PROVISION, Computer.PERMISSIONS.find("Provision")); } + + @Test @Issue("JENKINS-37616") + public void ensureProvisionPermissionIsLoadable() throws Exception { + // Name introduced by JENKINS-37616 + Permission p = Permission.fromId("hudson.model.Computer.Provision"); + assertEquals("Provision", p.name); + } } -- GitLab From 1ca9c880fd64e2c8c4985ea027d6d4079edc1df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20Gond=C5=BEa?= Date: Fri, 7 Apr 2017 10:50:15 +0200 Subject: [PATCH 0100/2185] [FIXED JENKINS-40848] Introduce status indicator for skipped download job (#2705) * [FIXED JENKINS-40848] Introduce status indicator for skipped download job * [JENKINS-40848] Stick to existing code-flow as that is proved to work --- .../main/java/hudson/model/UpdateCenter.java | 1 - .../DownloadJob/Skipped/status.jelly | 28 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 core/src/main/resources/hudson/model/UpdateCenter/DownloadJob/Skipped/status.jelly diff --git a/core/src/main/java/hudson/model/UpdateCenter.java b/core/src/main/java/hudson/model/UpdateCenter.java index 5b3486e141..b1c6c0141b 100644 --- a/core/src/main/java/hudson/model/UpdateCenter.java +++ b/core/src/main/java/hudson/model/UpdateCenter.java @@ -1870,7 +1870,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas // Do this first so we can avoid duplicate downloads, too // check to see if the plugin is already installed at the same version and skip it LOGGER.info("Skipping duplicate install of: " + plugin.getDisplayName() + "@" + plugin.version); - //throw new Skipped(); // TODO set skipped once we have a status indicator for it return; } try { diff --git a/core/src/main/resources/hudson/model/UpdateCenter/DownloadJob/Skipped/status.jelly b/core/src/main/resources/hudson/model/UpdateCenter/DownloadJob/Skipped/status.jelly new file mode 100644 index 0000000000..a6c1d9429f --- /dev/null +++ b/core/src/main/resources/hudson/model/UpdateCenter/DownloadJob/Skipped/status.jelly @@ -0,0 +1,28 @@ + + + + + ${%Skipped} + -- GitLab From f3da0e498f2351f5ca96d2eda6331b832c4f7873 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 7 Apr 2017 10:22:06 -0400 Subject: [PATCH 0101/2185] Minor review comments from @oleg-nenashev. --- .../java/hudson/cli/PlainCLIProtocol.java | 28 +++++++++++++------ .../CLI/WarnWhenEnabled/message.properties | 3 +- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/cli/src/main/java/hudson/cli/PlainCLIProtocol.java b/cli/src/main/java/hudson/cli/PlainCLIProtocol.java index d27669a0c9..30bc94b432 100644 --- a/cli/src/main/java/hudson/cli/PlainCLIProtocol.java +++ b/cli/src/main/java/hudson/cli/PlainCLIProtocol.java @@ -53,23 +53,28 @@ class PlainCLIProtocol { /** One-byte operation to send to the other side. */ private enum Op { /** UTF-8 command name or argument. */ - ARG, + ARG(true), /** UTF-8 locale identifier. */ - LOCALE, + LOCALE(true), /** UTF-8 client encoding. */ - ENCODING, + ENCODING(true), /** Start running command. */ - START, + START(true), /** Exit code, as int. */ - EXIT, + EXIT(false), /** Chunk of stdin, as int length followed by bytes. */ - STDIN, + STDIN(true), /** EOF on stdin. */ - END_STDIN, + END_STDIN(true), /** Chunk of stdout. */ - STDOUT, + STDOUT(false), /** Chunk of stderr. */ - STDERR + STDERR(false); + /** True if sent from the client to the server; false if sent from the server to the client. */ + final boolean clientSide; + Op(boolean clientSide) { + this.clientSide = clientSide; + } } static abstract class EitherSide implements Closeable { @@ -150,6 +155,9 @@ class PlainCLIProtocol { // in case trick in CLIAction does not work LOGGER.log(Level.FINE, null, x); handleClose(); + } catch (RuntimeException x) { + LOGGER.log(Level.WARNING, null, x); + handleClose(); } } @@ -233,6 +241,7 @@ class PlainCLIProtocol { @Override protected final boolean handle(Op op, int framelen) throws IOException { assert Thread.currentThread() instanceof EitherSide.Reader; + assert op.clientSide; switch (op) { case ARG: onArg(dis.readUTF()); @@ -292,6 +301,7 @@ class PlainCLIProtocol { @Override protected boolean handle(Op op, int framelen) throws IOException { assert Thread.currentThread() instanceof EitherSide.Reader; + assert !op.clientSide; switch (op) { case EXIT: onExit(dis.readInt()); diff --git a/core/src/main/resources/jenkins/CLI/WarnWhenEnabled/message.properties b/core/src/main/resources/jenkins/CLI/WarnWhenEnabled/message.properties index a3cdc9388a..2b6c3980d5 100644 --- a/core/src/main/resources/jenkins/CLI/WarnWhenEnabled/message.properties +++ b/core/src/main/resources/jenkins/CLI/WarnWhenEnabled/message.properties @@ -22,4 +22,5 @@ blurb=\ Allowing Jenkins CLI to work in -remoting mode is considered dangerous and usually unnecessary. \ - You are advised to disable this mode. + You are advised to disable this mode. \ + Please refer to the CLI documentation for details. -- GitLab From 0f87e10a69d9429d288cce5c4f00b1345a401f49 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 7 Apr 2017 11:29:21 -0400 Subject: [PATCH 0102/2185] @oleg-nenashev requested this be decoupled from https://github.com/jenkinsci/sshd-module/pull/10. --- cli/pom.xml | 1 + cli/src/main/java/hudson/cli/CLI.java | 89 +----------- cli/src/main/java/hudson/cli/SSHCLI.java | 132 ++++++++++++++++++ pom.xml | 5 - test/pom.xml | 5 - test/src/test/java/hudson/cli/CLITest.java | 5 +- .../java/hudson/cli/GetJobCommandTest.java | 1 + war/pom.xml | 2 +- 8 files changed, 141 insertions(+), 99 deletions(-) create mode 100644 cli/src/main/java/hudson/cli/SSHCLI.java diff --git a/cli/pom.xml b/cli/pom.xml index 5730bfbc20..9d8fe69339 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -56,6 +56,7 @@ org.apache.sshd sshd-core + 1.2.0 true diff --git a/cli/src/main/java/hudson/cli/CLI.java b/cli/src/main/java/hudson/cli/CLI.java index 2f6e15e0f4..c9d7149ca3 100644 --- a/cli/src/main/java/hudson/cli/CLI.java +++ b/cli/src/main/java/hudson/cli/CLI.java @@ -32,8 +32,6 @@ import hudson.remoting.RemoteInputStream; import hudson.remoting.RemoteOutputStream; import hudson.remoting.SocketChannelStream; import hudson.remoting.SocketOutputStream; -import hudson.util.QuotedStringTokenizer; - import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.HostnameVerifier; @@ -58,8 +56,6 @@ import java.io.StringReader; import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.Socket; -import java.net.SocketAddress; -import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLConnection; import java.nio.charset.Charset; @@ -74,7 +70,6 @@ import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Properties; -import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Handler; @@ -82,18 +77,6 @@ import java.util.logging.Level; import java.util.logging.Logger; import static java.util.logging.Level.*; import org.apache.commons.io.FileUtils; -import org.apache.sshd.client.SshClient; -import org.apache.sshd.client.channel.ClientChannel; -import org.apache.sshd.client.channel.ClientChannelEvent; -import org.apache.sshd.client.future.ConnectFuture; -import org.apache.sshd.client.keyverifier.DefaultKnownHostsServerKeyVerifier; -import org.apache.sshd.client.keyverifier.KnownHostsServerKeyVerifier; -import org.apache.sshd.client.keyverifier.ServerKeyVerifier; -import org.apache.sshd.client.session.ClientSession; -import org.apache.sshd.common.future.WaitableFuture; -import org.apache.sshd.common.util.SecurityUtils; -import org.apache.sshd.common.util.io.NoCloseInputStream; -import org.apache.sshd.common.util.io.NoCloseOutputStream; /** * CLI entry point to Jenkins. @@ -584,7 +567,7 @@ public class CLI implements AutoCloseable { LOGGER.warning("-user required when using -ssh"); return -1; } - return sshConnection(url, user, args, provider, strictHostKey); + return SSHCLI.sshConnection(url, user, args, provider, strictHostKey); } if (strictHostKey) { @@ -642,74 +625,6 @@ public class CLI implements AutoCloseable { } } - private static int sshConnection(String jenkinsUrl, String user, List args, PrivateKeyProvider provider, final boolean strictHostKey) throws IOException { - Logger.getLogger(SecurityUtils.class.getName()).setLevel(Level.WARNING); // suppress: BouncyCastle not registered, using the default JCE provider - URL url = new URL(jenkinsUrl + "login"); - URLConnection conn = url.openConnection(); - String endpointDescription = conn.getHeaderField("X-SSH-Endpoint"); - - if (endpointDescription == null) { - LOGGER.warning("No header 'X-SSH-Endpoint' returned by Jenkins"); - return -1; - } - - LOGGER.log(FINE, "Connecting via SSH to: {0}", endpointDescription); - - int sshPort = Integer.parseInt(endpointDescription.split(":")[1]); - String sshHost = endpointDescription.split(":")[0]; - - StringBuilder command = new StringBuilder(); - - for (String arg : args) { - command.append(QuotedStringTokenizer.quote(arg)); - command.append(' '); - } - - try(SshClient client = SshClient.setUpDefaultClient()) { - - KnownHostsServerKeyVerifier verifier = new DefaultKnownHostsServerKeyVerifier(new ServerKeyVerifier() { - @Override - public boolean verifyServerKey(ClientSession clientSession, SocketAddress remoteAddress, PublicKey serverKey) { - LOGGER.log(Level.WARNING, "Unknown host key for {0}", remoteAddress.toString()); - return !strictHostKey; - } - }, true); - - client.setServerKeyVerifier(verifier); - client.start(); - - ConnectFuture cf = client.connect(user, sshHost, sshPort); - cf.await(); - try (ClientSession session = cf.getSession()) { - for (KeyPair pair : provider.getKeys()) { - LOGGER.log(FINE, "Offering {0} private key", pair.getPrivate().getAlgorithm()); - session.addPublicKeyIdentity(pair); - } - session.auth().verify(10000L); - - try (ClientChannel channel = session.createExecChannel(command.toString())) { - channel.setIn(new NoCloseInputStream(System.in)); - channel.setOut(new NoCloseOutputStream(System.out)); - channel.setErr(new NoCloseOutputStream(System.err)); - WaitableFuture wf = channel.open(); - wf.await(); - - Set waitMask = channel.waitFor(Collections.singletonList(ClientChannelEvent.CLOSED), 0L); - - if(waitMask.contains(ClientChannelEvent.TIMEOUT)) { - throw new SocketTimeoutException("Failed to retrieve command result in time: " + command); - } - - Integer exitStatus = channel.getExitStatus(); - return exitStatus; - - } - } finally { - client.stop(); - } - } - } - private static int plainHttpConnection(String url, List args, CLIConnectionFactory factory) throws IOException, InterruptedException { LOGGER.log(FINE, "Trying to connect to {0} via plain protocol over HTTP", url); FullDuplexHttpStream streams = new FullDuplexHttpStream(new URL(url), "cli?remoting=false", factory.authorization); @@ -861,5 +776,5 @@ public class CLI implements AutoCloseable { System.err.println(Messages.CLI_Usage()); } - private static final Logger LOGGER = Logger.getLogger(CLI.class.getName()); + static final Logger LOGGER = Logger.getLogger(CLI.class.getName()); } diff --git a/cli/src/main/java/hudson/cli/SSHCLI.java b/cli/src/main/java/hudson/cli/SSHCLI.java new file mode 100644 index 0000000000..8a2ecc4f38 --- /dev/null +++ b/cli/src/main/java/hudson/cli/SSHCLI.java @@ -0,0 +1,132 @@ +/* + * The MIT License + * + * Copyright 2017 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 hudson.cli; + +import hudson.util.QuotedStringTokenizer; +import java.io.IOException; +import java.net.SocketAddress; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLConnection; +import java.security.KeyPair; +import java.security.PublicKey; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import static java.util.logging.Level.FINE; +import java.util.logging.Logger; +import org.apache.sshd.client.SshClient; +import org.apache.sshd.client.channel.ClientChannel; +import org.apache.sshd.client.channel.ClientChannelEvent; +import org.apache.sshd.client.future.ConnectFuture; +import org.apache.sshd.client.keyverifier.DefaultKnownHostsServerKeyVerifier; +import org.apache.sshd.client.keyverifier.KnownHostsServerKeyVerifier; +import org.apache.sshd.client.keyverifier.ServerKeyVerifier; +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.common.future.WaitableFuture; +import org.apache.sshd.common.util.SecurityUtils; +import org.apache.sshd.common.util.io.NoCloseInputStream; +import org.apache.sshd.common.util.io.NoCloseOutputStream; + +/** + * Implements SSH connection mode of {@link CLI}. + * In a separate class to avoid any class loading of {@code sshd-core} when not using {@code -ssh} mode. + * That allows the {@code test} module to pick up a version of {@code sshd-core} from the {@code sshd} module via {@code jenkins-war} + * that may not match the version being used from the {@code cli} module and may not be compatible. + */ +class SSHCLI { + + static int sshConnection(String jenkinsUrl, String user, List args, PrivateKeyProvider provider, final boolean strictHostKey) throws IOException { + Logger.getLogger(SecurityUtils.class.getName()).setLevel(Level.WARNING); // suppress: BouncyCastle not registered, using the default JCE provider + URL url = new URL(jenkinsUrl + "login"); + URLConnection conn = url.openConnection(); + String endpointDescription = conn.getHeaderField("X-SSH-Endpoint"); + + if (endpointDescription == null) { + CLI.LOGGER.warning("No header 'X-SSH-Endpoint' returned by Jenkins"); + return -1; + } + + CLI.LOGGER.log(FINE, "Connecting via SSH to: {0}", endpointDescription); + + int sshPort = Integer.parseInt(endpointDescription.split(":")[1]); + String sshHost = endpointDescription.split(":")[0]; + + StringBuilder command = new StringBuilder(); + + for (String arg : args) { + command.append(QuotedStringTokenizer.quote(arg)); + command.append(' '); + } + + try(SshClient client = SshClient.setUpDefaultClient()) { + + KnownHostsServerKeyVerifier verifier = new DefaultKnownHostsServerKeyVerifier(new ServerKeyVerifier() { + @Override + public boolean verifyServerKey(ClientSession clientSession, SocketAddress remoteAddress, PublicKey serverKey) { + CLI.LOGGER.log(Level.WARNING, "Unknown host key for {0}", remoteAddress.toString()); + return !strictHostKey; + } + }, true); + + client.setServerKeyVerifier(verifier); + client.start(); + + ConnectFuture cf = client.connect(user, sshHost, sshPort); + cf.await(); + try (ClientSession session = cf.getSession()) { + for (KeyPair pair : provider.getKeys()) { + CLI.LOGGER.log(FINE, "Offering {0} private key", pair.getPrivate().getAlgorithm()); + session.addPublicKeyIdentity(pair); + } + session.auth().verify(10000L); + + try (ClientChannel channel = session.createExecChannel(command.toString())) { + channel.setIn(new NoCloseInputStream(System.in)); + channel.setOut(new NoCloseOutputStream(System.out)); + channel.setErr(new NoCloseOutputStream(System.err)); + WaitableFuture wf = channel.open(); + wf.await(); + + Set waitMask = channel.waitFor(Collections.singletonList(ClientChannelEvent.CLOSED), 0L); + + if(waitMask.contains(ClientChannelEvent.TIMEOUT)) { + throw new SocketTimeoutException("Failed to retrieve command result in time: " + command); + } + + Integer exitStatus = channel.getExitStatus(); + return exitStatus; + + } + } finally { + client.stop(); + } + } + } + + private SSHCLI() {} + +} diff --git a/pom.xml b/pom.xml index f6a2b4e9da..f7ef5f54b8 100644 --- a/pom.xml +++ b/pom.xml @@ -223,11 +223,6 @@ THE SOFTWARE. jcifs 1.3.17-kohsuke-1 - - org.apache.sshd - sshd-core - 1.2.0 - diff --git a/test/pom.xml b/test/pom.xml index 9ff6dee2fc..f87e530d72 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -191,11 +191,6 @@ THE SOFTWARE. 4.0 test - - org.apache.sshd - sshd-core - test - diff --git a/test/src/test/java/hudson/cli/CLITest.java b/test/src/test/java/hudson/cli/CLITest.java index db4613fa70..19abf44b8f 100644 --- a/test/src/test/java/hudson/cli/CLITest.java +++ b/test/src/test/java/hudson/cli/CLITest.java @@ -25,6 +25,7 @@ package hudson.cli; import com.google.common.collect.Lists; +import hudson.Functions; import hudson.Launcher; import hudson.Proc; import hudson.model.FreeStyleProject; @@ -41,7 +42,6 @@ import jenkins.model.Jenkins; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.TeeOutputStream; -import org.apache.sshd.common.util.io.ModifiableFileWatcher; import static org.hamcrest.Matchers.*; import org.jenkinsci.main.modules.cli.auth.ssh.UserPropertyImpl; import org.jenkinsci.main.modules.sshd.SSHD; @@ -82,8 +82,11 @@ public class CLITest { } catch (IOException x) { assumeNoException("Sometimes on Windows KnownHostsServerKeyVerifier.acceptIncompleteHostKeys says WARNING: Failed (FileSystemException) to reload server keys from …\\\\.ssh\\\\known_hosts: … Incorrect function.", x); } + /* TODO impossible to do this until the bundled sshd module uses a sufficiently new version of sshd-core: assumeThat("or on Windows DefaultKnownHostsServerKeyVerifier.reloadKnownHosts says invalid file permissions: Owner violation (Administrators)", ModifiableFileWatcher.validateStrictConfigFilePermissions(known_hosts.toPath()), nullValue()); + */ + assumeFalse(Functions.isWindows()); // TODO can remove when above check is restored return home; } diff --git a/test/src/test/java/hudson/cli/GetJobCommandTest.java b/test/src/test/java/hudson/cli/GetJobCommandTest.java index 8a52cfed0c..878cb02248 100644 --- a/test/src/test/java/hudson/cli/GetJobCommandTest.java +++ b/test/src/test/java/hudson/cli/GetJobCommandTest.java @@ -47,6 +47,7 @@ public class GetJobCommandTest { FreeStyleProject p = d.createProject(FreeStyleProject.class, "p"); ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream outS = new PrintStream(out); + // TODO switch to CLICommandInvoker int result = new GetJobCommand().main(Collections.singletonList("d/p"), Locale.ENGLISH, new NullInputStream(0), outS, outS); outS.flush(); String output = out.toString(); diff --git a/war/pom.xml b/war/pom.xml index 74bc944928..2a31a14287 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -134,7 +134,7 @@ THE SOFTWARE. org.jenkins-ci.modules sshd - 1.11-20170324.200647-2 + 1.11-20170407.145808-3 org.jenkins-ci.ui -- GitLab From 98f227c20a3b8ebee6b8d8a259aea110ee65f6d7 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 7 Apr 2017 11:47:59 -0400 Subject: [PATCH 0103/2185] Showing general help message rather than suggesting obsolete authentication options. --- core/src/main/java/hudson/cli/HelpCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/cli/HelpCommand.java b/core/src/main/java/hudson/cli/HelpCommand.java index 295e6e0e8e..60fcc0970b 100644 --- a/core/src/main/java/hudson/cli/HelpCommand.java +++ b/core/src/main/java/hudson/cli/HelpCommand.java @@ -53,7 +53,7 @@ public class HelpCommand extends CLICommand { protected int run() throws Exception { if (!Jenkins.getActiveInstance().hasPermission(Jenkins.READ)) { throw new AccessDeniedException("You must authenticate to access this Jenkins.\n" - + "Use --username/--password/--password-file parameters or login command."); + + hudson.cli.client.Messages.CLI_Usage()); } if (command != null) -- GitLab From 45092f06a8a7e755268934091eb6da1ebf868faa Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 7 Apr 2017 12:04:58 -0400 Subject: [PATCH 0104/2185] checkChannel() provides a better error message in case you are not using Remoting mode. --- .../hudson/security/AbstractPasswordBasedSecurityRealm.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java b/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java index aacd95fdf7..dffde0d45e 100644 --- a/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java +++ b/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java @@ -69,12 +69,12 @@ public abstract class AbstractPasswordBasedSecurityRealm extends SecurityRealm i if (passwordFile!=null) try { - password = new FilePath(command.channel,passwordFile).readToString().trim(); + password = new FilePath(command.checkChannel(), passwordFile).readToString().trim(); } catch (IOException e) { throw new BadCredentialsException("Failed to read "+passwordFile,e); } if (password==null) - password = command.channel.call(new InteractivelyAskForPassword()); + password = command.checkChannel().call(new InteractivelyAskForPassword()); if (password==null) throw new BadCredentialsException("No password specified"); -- GitLab From 27d508d8d650b2906b2aa4c7547497cd5d7392a7 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 7 Apr 2017 12:21:32 -0400 Subject: [PATCH 0105/2185] sshd 1.11 --- war/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/war/pom.xml b/war/pom.xml index 2a31a14287..f19d9b3255 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -134,7 +134,7 @@ THE SOFTWARE. org.jenkins-ci.modules sshd - 1.11-20170407.145808-3 + 1.11 org.jenkins-ci.ui -- GitLab From 23b0085f453454462542ae6e0fd67915b760ee4e Mon Sep 17 00:00:00 2001 From: Andrew Bayer Date: Fri, 7 Apr 2017 12:39:29 -0700 Subject: [PATCH 0106/2185] [FIXED JENKINS-42043] Catch and log RuntimeException in setNode Also make sure we don't mark the Computer as used so that we kill any executors that may be related to it somehow. --- core/src/main/java/hudson/model/AbstractCIBase.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/hudson/model/AbstractCIBase.java b/core/src/main/java/hudson/model/AbstractCIBase.java index cf487105c2..16744334e5 100644 --- a/core/src/main/java/hudson/model/AbstractCIBase.java +++ b/core/src/main/java/hudson/model/AbstractCIBase.java @@ -36,6 +36,7 @@ import org.kohsuke.stapler.StaplerProxy; import java.util.*; import java.util.concurrent.CopyOnWriteArraySet; +import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.CheckForNull; @@ -115,7 +116,12 @@ public abstract class AbstractCIBase extends Node implements ItemGroup0 || n==Jenkins.getInstance()) { @@ -131,8 +137,8 @@ public abstract class AbstractCIBase extends Node implements ItemGroup Date: Fri, 7 Apr 2017 16:17:26 -0400 Subject: [PATCH 0107/2185] Revert "Noting that ssh-cli-auth is obsolete." This reverts commit 5caee586974271813bb833591726a693469dfe1f. In fact ssh-cli-auth contains UserPropertyImpl which is used by the sshd module. --- war/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/war/pom.xml b/war/pom.xml index f19d9b3255..24c79dd050 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -101,7 +101,7 @@ THE SOFTWARE. instance-identity 2.1 - + org.jenkins-ci.modules ssh-cli-auth 1.2 -- GitLab From 23cb13aad0f5f5015ca68ca00ca57ba093fa57ec Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 7 Apr 2017 18:41:27 -0400 Subject: [PATCH 0108/2185] Clean up usages of IOUtils.closeQuietly and related calls (#2830) * Clean up usages of IOUtils.closeQuietly and related calls predating Java 7. * Review comments from @oleg-nenashev. * Noticed another place where try-with-resources would protect against a potential leak. --- core/src/main/java/hudson/FilePath.java | 38 +++++------------ core/src/main/java/hudson/Main.java | 6 +-- .../java/hudson/TcpSlaveAgentListener.java | 42 ++++++++----------- core/src/main/java/hudson/Util.java | 27 ++++++++---- core/src/main/java/hudson/XmlFile.java | 5 +-- .../main/java/hudson/cli/ConsoleCommand.java | 6 +-- .../main/java/hudson/model/Descriptor.java | 5 +-- .../hudson/model/DirectoryBrowserSupport.java | 7 +--- .../java/hudson/model/FileParameterValue.java | 7 +--- core/src/main/java/hudson/model/Run.java | 18 +++----- .../main/java/hudson/model/UpdateCenter.java | 29 +++++-------- .../java/hudson/model/UsageStatistics.java | 8 ++-- core/src/main/java/hudson/scm/SCM.java | 8 +--- .../java/hudson/slaves/SlaveComputer.java | 6 ++- .../main/java/hudson/tools/JDKInstaller.java | 8 +--- .../main/java/hudson/triggers/SCMTrigger.java | 10 ++--- .../main/java/hudson/util/CompressedFile.java | 12 +++--- core/src/main/java/hudson/util/IOUtils.java | 8 ++-- .../hudson/util/MultipartFormDataParser.java | 8 +++- core/src/main/java/hudson/util/TextFile.java | 7 +--- .../java/jenkins/diagnosis/HsErrPidList.java | 8 ++-- core/src/main/java/jenkins/model/Jenkins.java | 18 +++----- .../security/DefaultConfidentialStore.java | 28 +++++-------- .../slaves/DefaultJnlpSlaveReceiver.java | 10 ++--- .../model/DirectoryBrowserSupportTest.java | 9 ++-- 25 files changed, 131 insertions(+), 207 deletions(-) diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index a06681ed19..445a4086a0 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -61,7 +61,6 @@ import hudson.util.io.ArchiverFactory; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileFilter; -import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; @@ -728,7 +727,7 @@ public final class FilePath implements Serializable { private static final long serialVersionUID = 1L; }); } finally { - org.apache.commons.io.IOUtils.closeQuietly(_in); + _in.close(); } } @@ -1760,15 +1759,11 @@ public final class FilePath implements Serializable { @Override public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { - InputStream fis = null; - try { - fis = Files.newInputStream(reading(f).toPath()); - Util.copyStream(fis, p.getOut()); + try (InputStream fis = Files.newInputStream(reading(f).toPath()); + OutputStream out = p.getOut()) { + org.apache.commons.io.IOUtils.copy(fis, out); } catch (Exception x) { p.error(x); - } finally { - org.apache.commons.io.IOUtils.closeQuietly(fis); - org.apache.commons.io.IOUtils.closeQuietly(p.getOut()); } return null; } @@ -1822,10 +1817,9 @@ public final class FilePath implements Serializable { private static final long serialVersionUID = 1L; public Void invoke(File f, VirtualChannel channel) throws IOException { - final OutputStream out = new java.util.zip.GZIPOutputStream(p.getOut(), 8192); - RandomAccessFile raf = null; - try { - raf = new RandomAccessFile(reading(f), "r"); + try (OutputStream os = p.getOut(); + OutputStream out = new java.util.zip.GZIPOutputStream(os, 8192); + RandomAccessFile raf = new RandomAccessFile(reading(f), "r")) { raf.seek(offset); byte[] buf = new byte[8192]; int len; @@ -1833,15 +1827,6 @@ public final class FilePath implements Serializable { out.write(buf, 0, len); } return null; - } finally { - IOUtils.closeQuietly(out); - if (raf != null) { - try { - raf.close(); - } catch (IOException e) { - // ignore - } - } } } }); @@ -2006,14 +1991,11 @@ public final class FilePath implements Serializable { act(new SecureFileCallable() { private static final long serialVersionUID = 4088559042349254141L; public Void invoke(File f, VirtualChannel channel) throws IOException { - InputStream fis = null; - try { - fis = Files.newInputStream(reading(f).toPath()); - Util.copyStream(fis,out); + try (InputStream fis = Files.newInputStream(reading(f).toPath())) { + org.apache.commons.io.IOUtils.copy(fis, out); return null; } finally { - org.apache.commons.io.IOUtils.closeQuietly(fis); - org.apache.commons.io.IOUtils.closeQuietly(out); + out.close(); } } }); diff --git a/core/src/main/java/hudson/Main.java b/core/src/main/java/hudson/Main.java index efe2ca0c9f..4abcf5c291 100644 --- a/core/src/main/java/hudson/Main.java +++ b/core/src/main/java/hudson/Main.java @@ -33,8 +33,6 @@ import com.thoughtworks.xstream.core.util.Base64Encoder; import hudson.util.IOUtils; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; @@ -174,11 +172,11 @@ public class Main { con.connect(); // send the data try (InputStream in = Files.newInputStream(tmpFile.toPath())) { - Util.copyStream(in,con.getOutputStream()); + org.apache.commons.io.IOUtils.copy(in, con.getOutputStream()); } if(con.getResponseCode()!=200) { - Util.copyStream(con.getErrorStream(),System.err); + org.apache.commons.io.IOUtils.copy(con.getErrorStream(), System.err); } return ret; diff --git a/core/src/main/java/hudson/TcpSlaveAgentListener.java b/core/src/main/java/hudson/TcpSlaveAgentListener.java index 6bee598966..6f41f441bc 100644 --- a/core/src/main/java/hudson/TcpSlaveAgentListener.java +++ b/core/src/main/java/hudson/TcpSlaveAgentListener.java @@ -339,14 +339,11 @@ public final class TcpSlaveAgentListener extends Thread { @Override public void handle(Socket socket) throws IOException, InterruptedException { try { - OutputStream stream = socket.getOutputStream(); - try { + try (OutputStream stream = socket.getOutputStream()) { LOGGER.log(Level.FINE, "Received ping request from {0}", socket.getRemoteSocketAddress()); stream.write(ping); stream.flush(); LOGGER.log(Level.FINE, "Sent ping response to {0}", socket.getRemoteSocketAddress()); - } finally { - stream.close(); } } finally { socket.close(); @@ -355,29 +352,24 @@ public final class TcpSlaveAgentListener extends Thread { public boolean connect(Socket socket) throws IOException { try { - DataOutputStream out = null; - InputStream in = null; - try { - LOGGER.log(Level.FINE, "Requesting ping from {0}", socket.getRemoteSocketAddress()); - out = new DataOutputStream(socket.getOutputStream()); + LOGGER.log(Level.FINE, "Requesting ping from {0}", socket.getRemoteSocketAddress()); + try (DataOutputStream out = new DataOutputStream(socket.getOutputStream())) { out.writeUTF("Protocol:Ping"); - in = socket.getInputStream(); - byte[] response = new byte[ping.length]; - int responseLength = in.read(response); - if (responseLength == ping.length && Arrays.equals(response, ping)) { - LOGGER.log(Level.FINE, "Received ping response from {0}", socket.getRemoteSocketAddress()); - return true; - } else { - LOGGER.log(Level.FINE, "Expected ping response from {0} of {1} got {2}", new Object[]{ - socket.getRemoteSocketAddress(), - new String(ping, "UTF-8"), - new String(response, 0, responseLength, "UTF-8") - }); - return false; + try (InputStream in = socket.getInputStream()) { + byte[] response = new byte[ping.length]; + int responseLength = in.read(response); + if (responseLength == ping.length && Arrays.equals(response, ping)) { + LOGGER.log(Level.FINE, "Received ping response from {0}", socket.getRemoteSocketAddress()); + return true; + } else { + LOGGER.log(Level.FINE, "Expected ping response from {0} of {1} got {2}", new Object[]{ + socket.getRemoteSocketAddress(), + new String(ping, "UTF-8"), + new String(response, 0, responseLength, "UTF-8") + }); + return false; + } } - } finally { - IOUtils.closeQuietly(out); - IOUtils.closeQuietly(in); } } finally { socket.close(); diff --git a/core/src/main/java/hudson/Util.java b/core/src/main/java/hudson/Util.java index ed794ef358..f7758cbedb 100644 --- a/core/src/main/java/hudson/Util.java +++ b/core/src/main/java/hudson/Util.java @@ -25,7 +25,6 @@ package hudson; import jenkins.util.SystemProperties; import com.sun.jna.Native; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Proc.LocalProc; @@ -645,6 +644,10 @@ public class Util { } } + /** + * @deprecated Use {@link IOUtils#copy(InputStream, OutputStream)} + */ + @Deprecated public static void copyStream(@Nonnull InputStream in,@Nonnull OutputStream out) throws IOException { byte[] buf = new byte[8192]; int len; @@ -652,6 +655,10 @@ public class Util { out.write(buf,0,len); } + /** + * @deprecated Use {@link IOUtils#copy(Reader, Writer)} + */ + @Deprecated public static void copyStream(@Nonnull Reader in, @Nonnull Writer out) throws IOException { char[] buf = new char[8192]; int len; @@ -659,21 +666,23 @@ public class Util { out.write(buf,0,len); } + /** + * @deprecated Use {@link IOUtils#copy(InputStream, OutputStream)} in a {@code try}-with-resources block + */ + @Deprecated public static void copyStreamAndClose(@Nonnull InputStream in, @Nonnull OutputStream out) throws IOException { - try { + try (InputStream _in = in; OutputStream _out = out) { // make sure both are closed, and use Throwable.addSuppressed copyStream(in,out); - } finally { - IOUtils.closeQuietly(in); - IOUtils.closeQuietly(out); } } + /** + * @deprecated Use {@link IOUtils#copy(Reader, Writer)} in a {@code try}-with-resources block + */ + @Deprecated public static void copyStreamAndClose(@Nonnull Reader in, @Nonnull Writer out) throws IOException { - try { + try (Reader _in = in; Writer _out = out) { copyStream(in,out); - } finally { - IOUtils.closeQuietly(in); - IOUtils.closeQuietly(out); } } diff --git a/core/src/main/java/hudson/XmlFile.java b/core/src/main/java/hudson/XmlFile.java index a118163dec..ba1434b8f7 100644 --- a/core/src/main/java/hudson/XmlFile.java +++ b/core/src/main/java/hudson/XmlFile.java @@ -45,16 +45,15 @@ import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import java.io.BufferedInputStream; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.Writer; import java.io.StringWriter; -import java.io.UnsupportedEncodingException; import java.util.logging.Level; import java.util.logging.Logger; +import org.apache.commons.io.IOUtils; /** * Represents an XML data file that Jenkins uses as a data file. @@ -228,7 +227,7 @@ public final class XmlFile { */ public void writeRawTo(Writer w) throws IOException { try (Reader r = readRaw()) { - Util.copyStream(r, w); + IOUtils.copy(r, w); } } diff --git a/core/src/main/java/hudson/cli/ConsoleCommand.java b/core/src/main/java/hudson/cli/ConsoleCommand.java index f4c3f719b5..f1069b857a 100644 --- a/core/src/main/java/hudson/cli/ConsoleCommand.java +++ b/core/src/main/java/hudson/cli/ConsoleCommand.java @@ -7,7 +7,6 @@ import hudson.model.AbstractProject; import hudson.model.Item; import hudson.model.PermalinkProjectAction.Permalink; import org.kohsuke.args4j.Argument; -import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.Option; import java.io.IOException; @@ -117,8 +116,7 @@ public class ConsoleCommand extends CLICommand { } RingBuffer rb = new RingBuffer(); - InputStream in = run.getLogInputStream(); - try { + try (InputStream in = run.getLogInputStream()) { byte[] buf = new byte[4096]; int len; byte prev=0; @@ -137,8 +135,6 @@ public class ConsoleCommand extends CLICommand { } return rb.get(); - } finally { - org.apache.commons.io.IOUtils.closeQuietly(in); } } diff --git a/core/src/main/java/hudson/model/Descriptor.java b/core/src/main/java/hudson/model/Descriptor.java index 2cccca3765..d51fed6a55 100644 --- a/core/src/main/java/hudson/model/Descriptor.java +++ b/core/src/main/java/hudson/model/Descriptor.java @@ -938,12 +938,9 @@ public abstract class Descriptor> implements Saveable, if(url!=null) { // TODO: generalize macro expansion and perhaps even support JEXL rsp.setContentType("text/html;charset=UTF-8"); - InputStream in = url.openStream(); - try { + try (InputStream in = url.openStream()) { String literal = IOUtils.toString(in,"UTF-8"); rsp.getWriter().println(Util.replaceMacro(literal, Collections.singletonMap("rootURL",req.getContextPath()))); - } finally { - IOUtils.closeQuietly(in); } return; } diff --git a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java index 418efca879..5426c7842e 100644 --- a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java +++ b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java @@ -371,11 +371,8 @@ public final class DirectoryBrowserSupport implements HttpResponse { VirtualFile f = dir.child(n); e.setTime(f.lastModified()); zos.putNextEntry(e); - InputStream in = f.open(); - try { - Util.copyStream(in, zos); - } finally { - IOUtils.closeQuietly(in); + try (InputStream in = f.open()) { + IOUtils.copy(in, zos); } zos.closeEntry(); } diff --git a/core/src/main/java/hudson/model/FileParameterValue.java b/core/src/main/java/hudson/model/FileParameterValue.java index 80461ed214..edb59a8b8a 100644 --- a/core/src/main/java/hudson/model/FileParameterValue.java +++ b/core/src/main/java/hudson/model/FileParameterValue.java @@ -30,8 +30,6 @@ import hudson.tasks.BuildWrapper; import hudson.util.VariableResolver; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -206,8 +204,7 @@ public class FileParameterValue extends ParameterValue { AbstractBuild build = (AbstractBuild)request.findAncestor(AbstractBuild.class).getObject(); File fileParameter = getLocationUnderBuild(build); if (fileParameter.isFile()) { - InputStream data = Files.newInputStream(fileParameter.toPath()); - try { + try (InputStream data = Files.newInputStream(fileParameter.toPath())) { long lastModified = fileParameter.lastModified(); long contentLength = fileParameter.length(); if (request.hasParameter("view")) { @@ -215,8 +212,6 @@ public class FileParameterValue extends ParameterValue { } else { response.serveFile(request, data, lastModified, contentLength, originalFileName); } - } finally { - IOUtils.closeQuietly(data); } } } diff --git a/core/src/main/java/hudson/model/Run.java b/core/src/main/java/hudson/model/Run.java index 3bc6d67930..516d948a1c 100644 --- a/core/src/main/java/hudson/model/Run.java +++ b/core/src/main/java/hudson/model/Run.java @@ -66,8 +66,6 @@ import hudson.util.XStream2; import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -1398,11 +1396,8 @@ public abstract class Run ,RunT extends Run,RunT extends Run=0) { + try (OutputStream _out = Files.newOutputStream(tmp.toPath()); + OutputStream out = sha1 != null ? new DigestOutputStream(_out, sha1) : _out; + InputStream in = con.getInputStream(); + CountingInputStream cin = new CountingInputStream(in)) { + while ((len = cin.read(buf)) >= 0) { out.write(buf,0,len); - job.status = job.new Installing(total==-1 ? -1 : in.getCount()*100/total); + job.status = job.new Installing(total == -1 ? -1 : cin.getCount() * 100 / total); } } catch (IOException e) { throw new IOException("Failed to load "+src+" to "+tmp,e); } finally { - IOUtils.closeQuietly(out); t.setName(oldName); } @@ -1171,9 +1165,6 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas extraMessage = " (redirected to: " + con.getURL() + ")"; } throw new IOException2("Failed to download from "+src+extraMessage,e); - } finally { - IOUtils.closeQuietly(in); - IOUtils.closeQuietly(out); } } @@ -1262,7 +1253,9 @@ public class UpdateCenter extends AbstractModelObject implements Saveable, OnMas throw new HttpRetryException("Invalid response code (" + responseCode + ") from URL: " + url, responseCode); } } else { - Util.copyStreamAndClose(connection.getInputStream(),new NullOutputStream()); + try (InputStream is = connection.getInputStream()) { + IOUtils.copy(is, new NullOutputStream()); + } } } catch (SSLHandshakeException e) { if (e.getMessage().contains("PKIX path building failed")) diff --git a/core/src/main/java/hudson/model/UsageStatistics.java b/core/src/main/java/hudson/model/UsageStatistics.java index 1fdbcca892..22d4008915 100644 --- a/core/src/main/java/hudson/model/UsageStatistics.java +++ b/core/src/main/java/hudson/model/UsageStatistics.java @@ -28,7 +28,6 @@ import hudson.PluginWrapper; import hudson.Util; import hudson.Extension; import hudson.node_monitors.ArchitectureMonitor.DescriptorImpl; -import hudson.util.IOUtils; import hudson.util.Secret; import static hudson.util.TimeUnit2.DAYS; @@ -181,11 +180,10 @@ public class UsageStatistics extends PageDecorator { ByteArrayOutputStream baos = new ByteArrayOutputStream(); // json -> UTF-8 encode -> gzip -> encrypt -> base64 -> string - OutputStreamWriter w = new OutputStreamWriter(new GZIPOutputStream(new CombinedCipherOutputStream(baos,getKey(),"AES")), "UTF-8"); - try { + try (OutputStream cipheros = new CombinedCipherOutputStream(baos,getKey(),"AES"); + OutputStream zipos = new GZIPOutputStream(cipheros); + OutputStreamWriter w = new OutputStreamWriter(zipos, "UTF-8")) { o.write(w); - } finally { - IOUtils.closeQuietly(w); } return new String(Base64.encode(baos.toByteArray())); diff --git a/core/src/main/java/hudson/scm/SCM.java b/core/src/main/java/hudson/scm/SCM.java index 9afb80f9a1..bfa1aecffc 100644 --- a/core/src/main/java/hudson/scm/SCM.java +++ b/core/src/main/java/hudson/scm/SCM.java @@ -48,7 +48,6 @@ import hudson.security.Permission; import hudson.security.PermissionGroup; import hudson.security.PermissionScope; import hudson.tasks.Builder; -import hudson.util.IOUtils; import java.io.File; import java.io.FileWriter; import java.io.IOException; @@ -683,13 +682,8 @@ public abstract class SCM implements Describable, ExtensionPoint { * @since 1.568 */ protected final void createEmptyChangeLog(@Nonnull File changelogFile, @Nonnull TaskListener listener, @Nonnull String rootTag) throws IOException { - FileWriter w = null; - try { - w = new FileWriter(changelogFile); + try (FileWriter w = new FileWriter(changelogFile)) { w.write("<"+rootTag +"/>"); - w.close(); - } finally { - IOUtils.closeQuietly(w); } } diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java index 40e66914d7..a1ed233278 100644 --- a/core/src/main/java/hudson/slaves/SlaveComputer.java +++ b/core/src/main/java/hudson/slaves/SlaveComputer.java @@ -673,7 +673,11 @@ public class SlaveComputer extends Computer { protected void kill() { super.kill(); closeChannel(); - IOUtils.closeQuietly(log); + try { + log.close(); + } catch (IOException x) { + LOGGER.log(Level.WARNING, "Failed to close agent log", x); + } try { Util.deleteRecursive(getLogDir()); diff --git a/core/src/main/java/hudson/tools/JDKInstaller.java b/core/src/main/java/hudson/tools/JDKInstaller.java index 70e44a26c3..2147be1e63 100644 --- a/core/src/main/java/hudson/tools/JDKInstaller.java +++ b/core/src/main/java/hudson/tools/JDKInstaller.java @@ -32,7 +32,6 @@ import hudson.ProxyConfiguration; import hudson.Util; import hudson.model.DownloadService.Downloadable; import hudson.model.JDK; -import hudson.model.Job; import hudson.model.Node; import hudson.model.TaskListener; import hudson.util.ArgumentListBuilder; @@ -64,7 +63,6 @@ import javax.servlet.ServletException; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -185,11 +183,9 @@ public class JDKInstaller extends ToolInstaller { // so check if the file is gzipped, and if so, treat it accordingly byte[] header = new byte[2]; { - DataInputStream in = new DataInputStream(fs.read(jdkBundle)); - try { + try (InputStream is = fs.read(jdkBundle); + DataInputStream in = new DataInputStream(is)) { in.readFully(header); - } finally { - IOUtils.closeQuietly(in); } } diff --git a/core/src/main/java/hudson/triggers/SCMTrigger.java b/core/src/main/java/hudson/triggers/SCMTrigger.java index 74ae690f7c..1a18b32764 100644 --- a/core/src/main/java/hudson/triggers/SCMTrigger.java +++ b/core/src/main/java/hudson/triggers/SCMTrigger.java @@ -42,13 +42,13 @@ import hudson.scm.SCM; import hudson.scm.SCMDescriptor; import hudson.util.FlushProofOutputStream; import hudson.util.FormValidation; -import hudson.util.IOUtils; import hudson.util.NamingThreadFactory; import hudson.util.SequentialExecutionQueue; import hudson.util.StreamTaskListener; import hudson.util.TimeUnit2; import java.io.File; import java.io.IOException; +import java.io.OutputStream; import java.io.PrintStream; import java.nio.charset.Charset; import java.text.DateFormat; @@ -450,12 +450,10 @@ public class SCMTrigger extends Trigger { */ public void doPollingLog(StaplerRequest req, StaplerResponse rsp) throws IOException { rsp.setContentType("text/plain;charset=UTF-8"); - // Prevent jelly from flushing stream so Content-Length header can be added afterwards - FlushProofOutputStream out = new FlushProofOutputStream(rsp.getCompressedOutputStream(req)); - try { + try (OutputStream os = rsp.getCompressedOutputStream(req); + // Prevent jelly from flushing stream so Content-Length header can be added afterwards + FlushProofOutputStream out = new FlushProofOutputStream(os)) { getPollingLogText().writeLogTo(0, out); - } finally { - IOUtils.closeQuietly(out); } } diff --git a/core/src/main/java/hudson/util/CompressedFile.java b/core/src/main/java/hudson/util/CompressedFile.java index 9c083d1808..283ae4f566 100644 --- a/core/src/main/java/hudson/util/CompressedFile.java +++ b/core/src/main/java/hudson/util/CompressedFile.java @@ -25,7 +25,6 @@ package hudson.util; import com.jcraft.jzlib.GZIPInputStream; import com.jcraft.jzlib.GZIPOutputStream; -import hudson.Util; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -112,14 +111,12 @@ public class CompressedFile { StringBuilder str = new StringBuilder((int)sizeGuess); - Reader r = new InputStreamReader(read()); - try { + try (InputStream is = read(); + Reader r = new InputStreamReader(is)) { char[] buf = new char[8192]; int len; while((len=r.read(buf,0,buf.length))>0) str.append(buf,0,len); - } finally { - IOUtils.closeQuietly(r); } return str.toString(); @@ -137,8 +134,9 @@ public class CompressedFile { public void run() { try { try (InputStream in = read(); - OutputStream out = new GZIPOutputStream(Files.newOutputStream(gz.toPath()))) { - Util.copyStream(in, out); + OutputStream os = Files.newOutputStream(gz.toPath()); + OutputStream out = new GZIPOutputStream(os)) { + org.apache.commons.io.IOUtils.copy(in, out); } // if the compressed file is created successfully, remove the original file.delete(); diff --git a/core/src/main/java/hudson/util/IOUtils.java b/core/src/main/java/hudson/util/IOUtils.java index db5062b888..564b678f25 100644 --- a/core/src/main/java/hudson/util/IOUtils.java +++ b/core/src/main/java/hudson/util/IOUtils.java @@ -183,7 +183,7 @@ public class IOUtils { } /** - * @deprecated Use instead {@link org.apache.commons.io.IOUtils#closeQuietly(java.io.Reader)} + * @deprecated Use Java 7 {@code try}-with-resources instead. */ @Deprecated public static void closeQuietly(Reader input) { @@ -191,7 +191,7 @@ public class IOUtils { } /** - * @deprecated Use instead {@link org.apache.commons.io.IOUtils#closeQuietly(java.io.Writer)} + * @deprecated Use Java 7 {@code try}-with-resources instead. */ @Deprecated public static void closeQuietly(Writer output) { @@ -199,7 +199,7 @@ public class IOUtils { } /** - * @deprecated Use instead {@link org.apache.commons.io.IOUtils#closeQuietly(java.io.InputStream)} + * @deprecated Use Java 7 {@code try}-with-resources instead. */ @Deprecated public static void closeQuietly(InputStream input) { @@ -207,7 +207,7 @@ public class IOUtils { } /** - * @deprecated Use instead {@link org.apache.commons.io.IOUtils#closeQuietly(java.io.OutputStream)} + * @deprecated Use Java 7 {@code try}-with-resources instead. */ @Deprecated public static void closeQuietly(OutputStream output) { diff --git a/core/src/main/java/hudson/util/MultipartFormDataParser.java b/core/src/main/java/hudson/util/MultipartFormDataParser.java index 1789759d12..a09a153046 100644 --- a/core/src/main/java/hudson/util/MultipartFormDataParser.java +++ b/core/src/main/java/hudson/util/MultipartFormDataParser.java @@ -41,7 +41,7 @@ import java.util.HashMap; * * @author Kohsuke Kawaguchi */ -public class MultipartFormDataParser { +public class MultipartFormDataParser implements AutoCloseable { private final ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory()); private final Map byName = new HashMap(); @@ -73,6 +73,12 @@ public class MultipartFormDataParser { item.delete(); } + /** Alias for {@link #cleanUp}. */ + @Override + public void close() { + cleanUp(); + } + /** * Checks a Content-Type string to assert if it is "multipart/form-data". * diff --git a/core/src/main/java/hudson/util/TextFile.java b/core/src/main/java/hudson/util/TextFile.java index ccb9638194..af0414f1e5 100644 --- a/core/src/main/java/hudson/util/TextFile.java +++ b/core/src/main/java/hudson/util/TextFile.java @@ -29,7 +29,6 @@ import java.nio.file.Files; import javax.annotation.Nonnull; import java.io.BufferedReader; import java.io.File; -import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; @@ -129,9 +128,7 @@ public class TextFile { public @Nonnull String head(int numChars) throws IOException { char[] buf = new char[numChars]; int read = 0; - Reader r = new FileReader(file); - - try { + try (Reader r = new FileReader(file)) { while (read Date: Sun, 9 Apr 2017 18:15:04 -0700 Subject: [PATCH 0109/2185] [maven-release-plugin] prepare release jenkins-2.54 --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index 9d8fe69339..7b108dfc52 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main pom - 2.54-SNAPSHOT + 2.54 cli diff --git a/core/pom.xml b/core/pom.xml index c9d2cee765..f3196be6e0 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.54-SNAPSHOT + 2.54 jenkins-core diff --git a/pom.xml b/pom.xml index f7ef5f54b8..dfcb10b0f0 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.54-SNAPSHOT + 2.54 pom Jenkins main module @@ -58,7 +58,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - HEAD + jenkins-2.54 diff --git a/test/pom.xml b/test/pom.xml index f87e530d72..1f5a752349 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.54-SNAPSHOT + 2.54 test diff --git a/war/pom.xml b/war/pom.xml index 24c79dd050..d6e38599d4 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.54-SNAPSHOT + 2.54 jenkins-war -- GitLab From cd13ba8bd8c73184592b24b0fe37644053952aa1 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Sun, 9 Apr 2017 18:15:04 -0700 Subject: [PATCH 0110/2185] [maven-release-plugin] prepare for next development iteration --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index 7b108dfc52..a313971d85 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main pom - 2.54 + 2.55-SNAPSHOT cli diff --git a/core/pom.xml b/core/pom.xml index f3196be6e0..a1a3d44cfa 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.54 + 2.55-SNAPSHOT jenkins-core diff --git a/pom.xml b/pom.xml index dfcb10b0f0..d85bbc4d09 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.54 + 2.55-SNAPSHOT pom Jenkins main module @@ -58,7 +58,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - jenkins-2.54 + HEAD diff --git a/test/pom.xml b/test/pom.xml index 1f5a752349..acc399a201 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.54 + 2.55-SNAPSHOT test diff --git a/war/pom.xml b/war/pom.xml index d6e38599d4..8b6265c66b 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.54 + 2.55-SNAPSHOT jenkins-war -- GitLab From 4b5fc6db6fc3f9626497d5bb3396085f22047084 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Mon, 10 Apr 2017 07:53:07 -0700 Subject: [PATCH 0111/2185] Disabling doclint in order to get the 2.54 release going Due to the switch to Java8, this issue that was previously non-blocking is now a release blocker. We generally don't check that the trunk is javadoc error safe, so requiring that only during the release process is likely going to cause this same problem over and over again --- pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pom.xml b/pom.xml index dfcb10b0f0..52f1296947 100644 --- a/pom.xml +++ b/pom.xml @@ -354,6 +354,9 @@ THE SOFTWARE. org.apache.maven.plugins maven-javadoc-plugin 2.10.3 + + -Xdoclint:none + org.apache.maven.plugins -- GitLab From 7c16d8dc46ff1d0226ca674d1f16d5d252905df6 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 11 Apr 2017 09:26:45 -0400 Subject: [PATCH 0112/2185] Fix Javadoc generation on JDK 8. --- Jenkinsfile | 2 +- .../java/hudson/cli/FullDuplexHttpStream.java | 2 +- .../main/java/hudson/AbstractMarkupText.java | 4 +-- .../java/hudson/ClassicPluginStrategy.java | 2 +- .../main/java/hudson/ExtensionListView.java | 1 + core/src/main/java/hudson/FilePath.java | 8 +++--- .../java/hudson/FileSystemProvisioner.java | 2 +- core/src/main/java/hudson/Functions.java | 6 ++--- core/src/main/java/hudson/MarkupText.java | 6 ++--- core/src/main/java/hudson/Platform.java | 2 +- core/src/main/java/hudson/Plugin.java | 4 +-- core/src/main/java/hudson/Proc.java | 2 +- .../java/hudson/diagnosis/OldDataMonitor.java | 2 +- .../diagnosis/ReverseProxySetupMonitor.java | 2 +- .../main/java/hudson/model/AbstractBuild.java | 4 +-- .../main/java/hudson/model/AbstractItem.java | 2 +- core/src/main/java/hudson/model/Action.java | 14 +++++----- .../hudson/model/AdministrativeMonitor.java | 1 + core/src/main/java/hudson/model/Build.java | 2 +- core/src/main/java/hudson/model/Computer.java | 3 ++- .../main/java/hudson/model/Descriptor.java | 2 +- .../hudson/model/EnvironmentContributor.java | 5 ++-- core/src/main/java/hudson/model/Executor.java | 2 +- .../java/hudson/model/ExecutorListener.java | 1 + .../main/java/hudson/model/HealthReport.java | 6 ++--- .../src/main/java/hudson/model/ItemGroup.java | 1 + .../java/hudson/model/ItemGroupMixIn.java | 2 +- core/src/main/java/hudson/model/Job.java | 2 +- .../hudson/model/JobPropertyDescriptor.java | 4 +-- core/src/main/java/hudson/model/Label.java | 12 ++++----- .../main/java/hudson/model/PageDecorator.java | 22 +++++++--------- .../hudson/model/ParameterDefinition.java | 7 +++-- .../java/hudson/model/ParameterValue.java | 8 +++--- .../main/java/hudson/model/PeriodicWork.java | 1 + core/src/main/java/hudson/model/Queue.java | 6 ++--- core/src/main/java/hudson/model/Run.java | 10 +++---- core/src/main/java/hudson/model/RunMap.java | 1 + core/src/main/java/hudson/model/Saveable.java | 1 + .../hudson/model/TopLevelItemDescriptor.java | 2 +- core/src/main/java/hudson/model/User.java | 2 +- core/src/main/java/hudson/model/View.java | 4 +-- .../java/hudson/model/ViewDescriptor.java | 2 +- .../java/hudson/model/ViewGroupMixIn.java | 9 ++++--- .../hudson/model/listeners/RunListener.java | 2 +- .../hudson/model/listeners/SCMListener.java | 2 +- .../java/hudson/model/queue/Executables.java | 2 +- .../hudson/model/queue/QueueListener.java | 1 + .../org/apache/tools/tar/TarOutputStream.java | 2 +- .../main/java/hudson/scheduler/CronTab.java | 2 +- core/src/main/java/hudson/scm/SCM.java | 10 +++---- .../security/BasicAuthenticationFilter.java | 6 ++--- .../java/hudson/security/PermissionAdder.java | 1 + .../java/hudson/security/SecurityRealm.java | 7 +++-- .../src/main/java/hudson/security/SidACL.java | 2 +- core/src/main/java/hudson/slaves/Cloud.java | 8 +++--- .../slaves/CloudProvisioningListener.java | 3 ++- .../src/main/java/hudson/tasks/BuildStep.java | 3 ++- core/src/main/java/hudson/tasks/Maven.java | 2 +- .../main/java/hudson/tools/JDKInstaller.java | 4 +-- .../main/java/hudson/triggers/SCMTrigger.java | 2 +- .../java/hudson/triggers/SafeTimerTask.java | 1 + .../java/hudson/util/ArgumentListBuilder.java | 26 +++++++++---------- .../java/hudson/util/ChunkedOutputStream.java | 2 +- .../java/hudson/util/CyclicGraphDetector.java | 2 +- .../java/hudson/util/DescribableList.java | 2 +- core/src/main/java/hudson/util/FormApply.java | 2 +- .../java/hudson/util/FormFieldValidator.java | 2 +- .../main/java/hudson/util/HttpResponses.java | 2 +- .../main/java/hudson/util/ListBoxModel.java | 12 ++++----- core/src/main/java/hudson/util/Memoizer.java | 2 +- .../java/hudson/util/NamingThreadFactory.java | 1 + .../java/hudson/util/QueryParameterMap.java | 2 +- .../java/hudson/util/jna/GNUCLibrary.java | 1 + .../java/hudson/util/spring/BeanBuilder.java | 8 +++--- .../hudson/util/spring/ClosureScript.java | 2 +- .../java/hudson/views/ListViewColumn.java | 2 +- .../java/hudson/widgets/HistoryWidget.java | 2 +- core/src/main/java/hudson/widgets/Widget.java | 4 +-- .../src/main/java/jenkins/FilePathFilter.java | 2 +- .../java/jenkins/model/ArtifactManager.java | 3 ++- .../java/jenkins/model/BuildDiscarder.java | 1 + .../jenkins/model/DependencyDeclarer.java | 1 + .../DirectlyModifiableTopLevelItemGroup.java | 2 ++ .../java/jenkins/model/FingerprintFacet.java | 3 +-- .../main/java/jenkins/model/IdStrategy.java | 2 +- core/src/main/java/jenkins/model/Jenkins.java | 3 ++- .../model/ModelObjectWithContextMenu.java | 4 +-- .../java/jenkins/model/PeepholePermalink.java | 2 +- .../java/jenkins/model/RunIdMigrator.java | 6 ++--- .../model/lazy/AbstractLazyLoadRunMap.java | 4 +-- .../model/queue/AsynchronousExecution.java | 4 +++ .../security/BasicHeaderProcessor.java | 1 - .../jenkins/security/ConfidentialKey.java | 1 + .../jenkins/security/ConfidentialStore.java | 2 ++ .../security/UpdateSiteWarningsMonitor.java | 2 +- .../jenkins/security/s2m/package-info.java | 2 +- .../java/jenkins/tasks/SimpleBuildStep.java | 6 +++++ .../jenkins/tasks/SimpleBuildWrapper.java | 1 + .../java/jenkins/util/SystemProperties.java | 2 +- .../main/java/jenkins/util/VirtualFile.java | 1 - .../jenkins/util/groovy/GroovyHookScript.java | 2 +- .../java/jenkins/util/xstream/XStreamDOM.java | 16 ++++++------ pom.xml | 5 +--- test/pom.xml | 6 +++++ 104 files changed, 213 insertions(+), 188 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index d7cc19de84..266bf5ce46 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -44,7 +44,7 @@ for(i = 0; i < buildTypes.size(); i++) { // The -Dmaven.repo.local=${pwd()}/.repository means that Maven will create a // .repository directory at the root of the build (which it gets from the // pwd() Workflow call) and use that for the local Maven repository. - def mvnCmd = "mvn -Pdebug -U clean install ${runTests ? '-Dmaven.test.failure.ignore=true' : '-DskipTests'} -V -B -Dmaven.repo.local=${pwd()}/.repository" + def mvnCmd = "mvn -Pdebug -U clean install javadoc:javadoc ${runTests ? '-Dmaven.test.failure.ignore=true' : '-DskipTests'} -V -B -Dmaven.repo.local=${pwd()}/.repository" if(isUnix()) { sh mvnCmd } else { diff --git a/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java b/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java index c01f3261ce..8017b44e41 100644 --- a/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java +++ b/cli/src/main/java/hudson/cli/FullDuplexHttpStream.java @@ -61,7 +61,7 @@ public class FullDuplexHttpStream { /** * @param base the base URL of Jenkins - * @param target + * @param relativeTarget * The endpoint that we are making requests to. * @param authorization * The value of the authorization header, if non-null. diff --git a/core/src/main/java/hudson/AbstractMarkupText.java b/core/src/main/java/hudson/AbstractMarkupText.java index ed7e319711..1e78b3e10c 100644 --- a/core/src/main/java/hudson/AbstractMarkupText.java +++ b/core/src/main/java/hudson/AbstractMarkupText.java @@ -72,8 +72,8 @@ public abstract class AbstractMarkupText { * Adds a start tag and end tag at the specified position. * *

- * For example, if the text was "abc", then addMarkup(1,2,"<b>","</b>") - * would generate "a<b>b</b>c" + * For example, if the text was "abc", then {@code addMarkup(1,2,"","")} + * would generate {@code"abc"} */ public abstract void addMarkup( int startPos, int endPos, String startTag, String endTag ); diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java index 17722cef44..2744f537e6 100644 --- a/core/src/main/java/hudson/ClassicPluginStrategy.java +++ b/core/src/main/java/hudson/ClassicPluginStrategy.java @@ -392,7 +392,7 @@ public class ClassicPluginStrategy implements PluginStrategy { * Gets the minimum required version for the current version of Jenkins. * * @return the minimum required version for the current version of Jenkins. - * @sice 2.16 + * @since 2.16 */ public VersionNumber getRequiredVersion() { return new VersionNumber(requiredVersion); diff --git a/core/src/main/java/hudson/ExtensionListView.java b/core/src/main/java/hudson/ExtensionListView.java index 59b81ffd95..100dad6fb7 100644 --- a/core/src/main/java/hudson/ExtensionListView.java +++ b/core/src/main/java/hudson/ExtensionListView.java @@ -23,6 +23,7 @@ */ package hudson; +import hudson.tasks.UserNameResolver; import jenkins.model.Jenkins; import hudson.util.CopyOnWriteList; diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 445a4086a0..985bf56e33 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -167,7 +167,7 @@ import static hudson.Util.isSymlink; * } * // if 'file' is on a different node, this FileCallable will * // be transferred to that node and executed there. - * private static final class Freshen implements FileCallable<Void> { + * private static final class Freshen implements FileCallable<Void> { * private static final long serialVersionUID = 1; * @Override public Void invoke(File f, VirtualChannel channel) { * // f and file represent the same thing @@ -933,7 +933,7 @@ public final class FilePath implements Serializable { /** * Code that gets executed on the machine where the {@link FilePath} is local. * Used to act on {@link FilePath}. - * Warning: implementations must be serializable, so prefer a static nested class to an inner class. + * Warning: implementations must be serializable, so prefer a static nested class to an inner class. * *

* Subtypes would likely want to extend from either {@link MasterToSlaveCallable} @@ -2314,7 +2314,7 @@ public final class FilePath implements Serializable { } /** - * Same as {@link #validateFileMask(String, int, boolean)} with caseSensitive set to true + * Same as {@link #validateAntFileMask(String, int, boolean)} with caseSensitive set to true */ public String validateAntFileMask(final String fileMasks, final int bound) throws IOException, InterruptedException { return validateAntFileMask(fileMasks, bound, true); @@ -2523,7 +2523,7 @@ public final class FilePath implements Serializable { } /** - * Shortcut for {@link #validateFileMask(String,true,boolean)} as the left-hand side can be null. + * Shortcut for {@link #validateFileMask(String,boolean,boolean)} with {@code errorIfNotExist} true, as the left-hand side can be null. */ public static FormValidation validateFileMask(@CheckForNull FilePath path, String value, boolean caseSensitive) throws IOException { if(path==null) return FormValidation.ok(); diff --git a/core/src/main/java/hudson/FileSystemProvisioner.java b/core/src/main/java/hudson/FileSystemProvisioner.java index 42d2b6ca53..5b13241fbe 100644 --- a/core/src/main/java/hudson/FileSystemProvisioner.java +++ b/core/src/main/java/hudson/FileSystemProvisioner.java @@ -53,7 +53,7 @@ import java.io.OutputStream; * STILL A WORK IN PROGRESS. SUBJECT TO CHANGE! DO NOT EXTEND. * * TODO: is this per {@link Computer}? Per {@link Job}? - * -> probably per agent. + * → probably per agent. * *

Design Problems

*
    diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index 9ff620a894..c63c6dbb14 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -1798,7 +1798,7 @@ public class Functions { } /** - * Generate a series of <script> tags to include script.js + * Generate a series of {@code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    Тих период
    Секунди
    Опити за изтеглÑне от ÑиÑтемата за контрол на верÑиите
    + + +
    + +
    + + +
    ДиректориÑ
    Показвано име
    + +
    + + + + + + +
    #Ðвтоматично изпълнÑвани дейÑÑ‚Ð²Ð¸Ñ Ð¿Ñ€Ð¸ изграждане
    + + + + + + + + + + + +
    #Изграждане
    + + + + +
    +
    +
    +
    + +
    +
    + + + + + + diff --git a/war/src/test/js/widgets/config/freestyle-config-tabbed_bg.html b/war/src/test/js/widgets/config/freestyle-config-tabbed_bg.html new file mode 100644 index 0000000000..9d5c3c8d79 --- /dev/null +++ b/war/src/test/js/widgets/config/freestyle-config-tabbed_bg.html @@ -0,0 +1,163 @@ +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Име на проект + + +
    + + +
    +
    Зареждане…
    +
    СтратегиÑ
    +
    #Допълнителни наÑтройки на проекта
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    Тих период
    Секунди
    Опити за изтеглÑне от ÑиÑтемата за контрол на верÑиите
    + + +
    + +
    + + +
    ДиректориÑ
    Показвано име
    + +
    +
    +
    #Ðвтоматично изпълнÑвани дейÑÑ‚Ð²Ð¸Ñ Ð¿Ñ€Ð¸ изграждане
    +
    + + + +
    +
    #Изграждане
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    -- GitLab From 5fcccf79faad6f607d277b434ef322e0495867bb Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Mon, 21 Aug 2017 07:44:04 -0700 Subject: [PATCH 0419/2185] [maven-release-plugin] prepare release jenkins-2.75 --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index b98eaaa433..0258e4bde5 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main pom - 2.75-SNAPSHOT + 2.75 cli diff --git a/core/pom.xml b/core/pom.xml index 3fcc54e1d6..187a34af55 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.75-SNAPSHOT + 2.75 jenkins-core diff --git a/pom.xml b/pom.xml index 7488ff1f29..9f0f52a776 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.75-SNAPSHOT + 2.75 pom Jenkins main module @@ -58,7 +58,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - HEAD + jenkins-2.75 diff --git a/test/pom.xml b/test/pom.xml index 9d12833b20..d2d05df3b5 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.75-SNAPSHOT + 2.75 test diff --git a/war/pom.xml b/war/pom.xml index 4203816bd0..6ae03d4122 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.75-SNAPSHOT + 2.75 jenkins-war -- GitLab From 8c580ddd866a86b9143f57f96dc6be6ef9d10cb4 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Mon, 21 Aug 2017 07:44:04 -0700 Subject: [PATCH 0420/2185] [maven-release-plugin] prepare for next development iteration --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index 0258e4bde5..a8d38aa350 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main pom - 2.75 + 2.76-SNAPSHOT cli diff --git a/core/pom.xml b/core/pom.xml index 187a34af55..04e5c819a4 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.75 + 2.76-SNAPSHOT jenkins-core diff --git a/pom.xml b/pom.xml index 9f0f52a776..063a3a11bb 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.75 + 2.76-SNAPSHOT pom Jenkins main module @@ -58,7 +58,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - jenkins-2.75 + HEAD diff --git a/test/pom.xml b/test/pom.xml index d2d05df3b5..d68cad1116 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.75 + 2.76-SNAPSHOT test diff --git a/war/pom.xml b/war/pom.xml index 6ae03d4122..a9812aba46 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.75 + 2.76-SNAPSHOT jenkins-war -- GitLab From c60735a1242b8b06817c2fd0feeb9dc868750948 Mon Sep 17 00:00:00 2001 From: Akbashev Alexander Date: Tue, 22 Aug 2017 10:33:10 +0200 Subject: [PATCH 0421/2185] [JENKINS-29537] EnvironmentContributingAction compatible with Workflow (#2975) * [JENKINS-29537] EnvironmentContributingAction compatible with Workflow + Adds new method with default implementation in EnvironmentContributingAction to support Runs + Marks AbstractBuild as deprecated + Adds default implementation for deprecated method for backward compatiblity. * Tiny improvements in javadoc --- .../main/java/hudson/model/AbstractBuild.java | 2 +- .../model/EnvironmentContributingAction.java | 37 +++- .../java/hudson/model/ParametersAction.java | 5 +- core/src/main/java/hudson/model/Run.java | 3 + .../EnvironmentContributingActionTest.java | 159 ++++++++++++++++++ .../hudson/model/ParametersActionTest.java | 2 +- 6 files changed, 203 insertions(+), 5 deletions(-) create mode 100644 core/src/test/java/hudson/model/EnvironmentContributingActionTest.java diff --git a/core/src/main/java/hudson/model/AbstractBuild.java b/core/src/main/java/hudson/model/AbstractBuild.java index c8674fb028..2b7775c304 100644 --- a/core/src/main/java/hudson/model/AbstractBuild.java +++ b/core/src/main/java/hudson/model/AbstractBuild.java @@ -887,7 +887,7 @@ public abstract class AbstractBuild

    ,R extends Abs e.buildEnvVars(env); for (EnvironmentContributingAction a : getActions(EnvironmentContributingAction.class)) - a.buildEnvVars(this,env); + a.buildEnvVars(this,env,getBuiltOn()); EnvVars.resolve(env); diff --git a/core/src/main/java/hudson/model/EnvironmentContributingAction.java b/core/src/main/java/hudson/model/EnvironmentContributingAction.java index 761ed8ae97..55f5035965 100644 --- a/core/src/main/java/hudson/model/EnvironmentContributingAction.java +++ b/core/src/main/java/hudson/model/EnvironmentContributingAction.java @@ -24,9 +24,15 @@ package hudson.model; import hudson.EnvVars; +import hudson.Util; import hudson.model.Queue.Task; import hudson.tasks.Builder; import hudson.tasks.BuildWrapper; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.ProtectedExternally; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; /** * {@link Action} that contributes environment variables during a build. @@ -44,13 +50,42 @@ import hudson.tasks.BuildWrapper; * @see BuildWrapper */ public interface EnvironmentContributingAction extends Action { + /** + * Called by {@link Run} or {@link AbstractBuild} to allow plugins to contribute environment variables. + * + * @param run + * The calling build. Never null. + * @param node + * The node execute on. Can be {@code null} when the Run is not binded to the node, + * e.g. in Pipeline outside the {@code node() step} + * @param env + * Environment variables should be added to this map. + * @since TODO + */ + default void buildEnvVars(@Nonnull Run run, @Nonnull EnvVars env, @CheckForNull Node node) { + if (run instanceof AbstractBuild + && Util.isOverridden(EnvironmentContributingAction.class, + getClass(), "buildEnvVars", AbstractBuild.class, EnvVars.class)) { + buildEnvVars((AbstractBuild) run, env); + } + } + /** * Called by {@link AbstractBuild} to allow plugins to contribute environment variables. * + * @deprecated Use {@link #buildEnvVars(Run, EnvVars, Node)} instead + * * @param build * The calling build. Never null. * @param env * Environment variables should be added to this map. */ - void buildEnvVars(AbstractBuild build, EnvVars env); + @Deprecated + @Restricted(ProtectedExternally.class) + default void buildEnvVars(AbstractBuild build, EnvVars env) { + if (Util.isOverridden(EnvironmentContributingAction.class, + getClass(), "buildEnvVars", Run.class, EnvVars.class, Node.class)) { + buildEnvVars(build, env, build.getBuiltOn()); + } + } } diff --git a/core/src/main/java/hudson/model/ParametersAction.java b/core/src/main/java/hudson/model/ParametersAction.java index 6f7ecca9f8..86e687b17f 100644 --- a/core/src/main/java/hudson/model/ParametersAction.java +++ b/core/src/main/java/hudson/model/ParametersAction.java @@ -138,10 +138,11 @@ public class ParametersAction implements RunAction2, Iterable, Q } } - public void buildEnvVars(AbstractBuild build, EnvVars env) { + @Override + public void buildEnvVars(Run run, EnvVars env, Node node) { for (ParameterValue p : getParameters()) { if (p == null) continue; - p.buildEnvironment(build, env); + p.buildEnvironment(run, env); } } diff --git a/core/src/main/java/hudson/model/Run.java b/core/src/main/java/hudson/model/Run.java index c467439347..46970882a2 100644 --- a/core/src/main/java/hudson/model/Run.java +++ b/core/src/main/java/hudson/model/Run.java @@ -2301,6 +2301,9 @@ public abstract class Run ,RunT extends Run run, EnvVars env, @CheckForNull Node node) { + wasCalled = true; + } + + boolean wasNewMethodCalled() { + return wasCalled; + } + } + + class OverrideAbstractBuild extends InvisibleAction implements EnvironmentContributingAction { + private boolean wasCalled = false; + + @Override + @SuppressWarnings("deprecation") + public void buildEnvVars(AbstractBuild abstractBuild, EnvVars envVars) { + wasCalled = true; + } + + boolean wasDeprecatedMethodCalled() { + return wasCalled; + } + } + + class OverrideBoth extends InvisibleAction implements EnvironmentContributingAction { + private boolean wasCalledAstractBuild = false; + private boolean wasCalledRun = false; + + @SuppressWarnings("deprecation") + @Override + public void buildEnvVars(AbstractBuild abstractBuild, EnvVars envVars) { + wasCalledAstractBuild = true; + } + + @Override + public void buildEnvVars(Run run, EnvVars env, @CheckForNull Node node) { + wasCalledRun = true; + } + + boolean wasDeprecatedMethodCalled() { + return wasCalledAstractBuild; + } + + boolean wasRunCalled() { + return wasCalledRun; + } + } + + private final EnvVars envVars = mock(EnvVars.class); + + @Test + public void testOverrideRunMethodAndCallNewMethod() throws Exception { + Run run = mock(Run.class); + Node node = mock(Node.class); + + OverrideRun overrideRun = new OverrideRun(); + overrideRun.buildEnvVars(run, envVars, node); + + assertTrue(overrideRun.wasNewMethodCalled()); + } + + /** + * If only non-deprecated method was overridden it would be executed even if someone would call deprecated method. + * @throws Exception if happens. + */ + @Test + @SuppressWarnings("deprecation") + public void testOverrideRunMethodAndCallDeprecatedMethod() throws Exception { + AbstractBuild abstractBuild = mock(AbstractBuild.class); + when(abstractBuild.getBuiltOn()).thenReturn(mock(Node.class)); + + OverrideRun overrideRun = new OverrideRun(); + overrideRun.buildEnvVars(abstractBuild, envVars); + + assertTrue(overrideRun.wasNewMethodCalled()); + } + + /** + * {@link AbstractBuild} should work as before. + * @throws Exception if happens. + */ + @Test + public void testOverrideAbstractBuildAndCallNewMethodWithAbstractBuild() throws Exception { + AbstractBuild abstractBuild = mock(AbstractBuild.class); + Node node = mock(Node.class); + + OverrideAbstractBuild action = new OverrideAbstractBuild(); + action.buildEnvVars(abstractBuild, envVars, node); + + assertTrue(action.wasDeprecatedMethodCalled()); + } + + /** + * {@link Run} should not execute method that was overridden for {@link AbstractBuild}. + * @throws Exception if happens. + */ + @Test + public void testOverrideAbstractBuildAndCallNewMethodWithRun() throws Exception { + Run run = mock(Run.class); + Node node = mock(Node.class); + + OverrideAbstractBuild action = new OverrideAbstractBuild(); + action.buildEnvVars(run, envVars, node); + + assertFalse(action.wasDeprecatedMethodCalled()); + } + + /** + * If someone wants to use overridden deprecated method, it would still work. + * @throws Exception if happens. + */ + @Test + public void testOverrideAbstractBuildAndCallDeprecatedMethod() throws Exception { + AbstractBuild abstractBuild = mock(AbstractBuild.class); + + OverrideAbstractBuild overrideRun = new OverrideAbstractBuild(); + overrideRun.buildEnvVars(abstractBuild, envVars); + + assertTrue(overrideRun.wasDeprecatedMethodCalled()); + } + + @Test + public void testOverrideBothAndCallNewMethod() throws Exception { + Run run = mock(Run.class); + Node node = mock(Node.class); + + OverrideBoth overrideRun = new OverrideBoth(); + overrideRun.buildEnvVars(run, envVars, node); + + assertTrue(overrideRun.wasRunCalled()); + } + + @Test + public void testOverrideBothAndCallDeprecatedMethod() throws Exception { + AbstractBuild abstractBuild = mock(AbstractBuild.class); + + OverrideBoth overrideRun = new OverrideBoth(); + overrideRun.buildEnvVars(abstractBuild, envVars); + + assertTrue(overrideRun.wasDeprecatedMethodCalled()); + } +} \ No newline at end of file diff --git a/core/src/test/java/hudson/model/ParametersActionTest.java b/core/src/test/java/hudson/model/ParametersActionTest.java index d9b6e63e69..182ead6ec1 100644 --- a/core/src/test/java/hudson/model/ParametersActionTest.java +++ b/core/src/test/java/hudson/model/ParametersActionTest.java @@ -105,7 +105,7 @@ public class ParametersActionTest { // Interaction with build EnvVars vars = new EnvVars(); - parametersAction.buildEnvVars(build, vars); + parametersAction.buildEnvVars(build, vars, build.getBuiltOn()); assertEquals(2, vars.size()); parametersAction.createVariableResolver(build); -- GitLab From c01a597f4790ce6962b21bd3ce057b07df68c82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20Gond=C5=BEa?= Date: Wed, 23 Aug 2017 12:54:59 +0200 Subject: [PATCH 0422/2185] Towards 2.73.1 --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index fbd4e27dd2..842f536da2 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main pom - 2.73 + 2.73.1-SNAPSHOT cli diff --git a/core/pom.xml b/core/pom.xml index 2cf8a6debd..1b4c11e152 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.73 + 2.73.1-SNAPSHOT jenkins-core diff --git a/pom.xml b/pom.xml index e4631ebaa8..740a6ec5a6 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.73 + 2.73.1-SNAPSHOT pom Jenkins main module @@ -58,7 +58,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - jenkins-2.73 + HEAD diff --git a/test/pom.xml b/test/pom.xml index 34b13bf24f..2561b1eb61 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.73 + 2.73.1-SNAPSHOT test diff --git a/war/pom.xml b/war/pom.xml index e02ef837ff..e1e4872633 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.73 + 2.73.1-SNAPSHOT jenkins-war -- GitLab From 9a8fdb9ac32702552f156d2898dffe2e67402c46 Mon Sep 17 00:00:00 2001 From: Josiah Haswell Date: Sat, 12 Aug 2017 16:53:41 -0600 Subject: [PATCH 0423/2185] [FIX JENKINS-43848] - Lack of cache-invalidation headers results in stale item list (#2973) * Saving progress for review * Adding licenses * Adding integration test for headers * Removing proposal for refactoring to method objects * Removing whitespace changeswq * Fixing test (cherry picked from commit 34bf393255bb603bb3b0fb921a41fc3916d16f42) --- core/src/main/java/hudson/model/View.java | 4 +++ test/src/test/java/hudson/model/ViewTest.java | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java index 375087c2da..f19ab9a605 100644 --- a/core/src/main/java/hudson/model/View.java +++ b/core/src/main/java/hudson/model/View.java @@ -1053,6 +1053,10 @@ public abstract class View extends AbstractModelObject implements AccessControll */ @Restricted(DoNotUse.class) public Categories doItemCategories(StaplerRequest req, StaplerResponse rsp, @QueryParameter String iconStyle) throws IOException, ServletException { + + rsp.addHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + rsp.addHeader("Pragma", "no-cache"); + rsp.addHeader("Expires", "0"); getOwner().checkPermission(Item.CREATE); Categories categories = new Categories(); int order = 0; diff --git a/test/src/test/java/hudson/model/ViewTest.java b/test/src/test/java/hudson/model/ViewTest.java index 56f99abdf0..7a70d87df2 100644 --- a/test/src/test/java/hudson/model/ViewTest.java +++ b/test/src/test/java/hudson/model/ViewTest.java @@ -25,6 +25,7 @@ package hudson.model; import com.gargoylesoftware.htmlunit.WebRequest; import com.gargoylesoftware.htmlunit.html.DomNodeUtil; +import com.gargoylesoftware.htmlunit.util.NameValuePair; import jenkins.model.Jenkins; import org.jvnet.hudson.test.Issue; import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; @@ -53,11 +54,15 @@ import hudson.util.HudsonIsLoading; import java.io.File; import java.io.IOException; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.logging.Level; import java.util.logging.LogRecord; import jenkins.model.ProjectNamingStrategy; import jenkins.security.NotReallyRoleSensitiveCallable; + +import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.*; import org.junit.Ignore; import org.junit.Rule; @@ -85,6 +90,26 @@ public class ViewTest { assertNotNull(j.createWebClient().goTo("").getWebResponse().getResponseHeaderValue("X-Hudson")); } + @Issue("JENKINS-43848") + @Test public void testNoCacheHeadersAreSet() throws Exception { + List responseHeaders = j.createWebClient() + .goTo("view/all/itemCategories", "application/json") + .getWebResponse() + .getResponseHeaders(); + + + final Map values = new HashMap<>(); + + for(NameValuePair p : responseHeaders) { + values.put(p.getName(), p.getValue()); + } + + String resp = values.get("Cache-Control"); + assertThat(resp, is("no-cache, no-store, must-revalidate")); + assertThat(values.get("Expires"), is("0")); + assertThat(values.get("Pragma"), is("no-cache")); + } + /** * Creating two views with the same name. */ -- GitLab From 4d2100d0845fc4de5c51105660e4be23fb16e868 Mon Sep 17 00:00:00 2001 From: godfath3r Date: Sat, 12 Aug 2017 13:24:32 +0300 Subject: [PATCH 0424/2185] [JENKINS-42376] - Add executor name on Unexpected exception. (#2970) * Add executor name on Unexpected exception. * Add a colon after job name (cherry picked from commit 86c28ea056236ee9125af7cc85b256cb65643a2f) --- core/src/main/java/hudson/model/Executor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/model/Executor.java b/core/src/main/java/hudson/model/Executor.java index 279a1fa762..a9b428ffba 100644 --- a/core/src/main/java/hudson/model/Executor.java +++ b/core/src/main/java/hudson/model/Executor.java @@ -444,7 +444,7 @@ public class Executor extends Thread implements ModelObject { LOGGER.log(FINE, getName()+" interrupted",e); // die peacefully } catch(Exception | Error e) { - LOGGER.log(SEVERE, "Unexpected executor death", e); + LOGGER.log(SEVERE, getName()+": Unexpected executor death", e); } finally { if (asynchronousExecution == null) { finish2(); -- GitLab From 12a949ea01fd4d03d7626de6c95afc828f79ac18 Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Fri, 25 Aug 2017 16:04:09 +0200 Subject: [PATCH 0425/2185] Update to Jenkins Parent POM 1.38 (#2985) * Update to Jenkins Parent POM 1.39 * Pick the released version of Jenkins POM --- pom.xml | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/pom.xml b/pom.xml index 063a3a11bb..27c6c9cc8d 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci jenkins - 1.36 + 1.38 org.jenkins-ci.main @@ -94,7 +94,6 @@ THE SOFTWARE. ${skipTests} 3.0.4 true - 1.2 1.11 ${access-modifier.version} ${access-modifier.version} @@ -239,14 +238,12 @@ THE SOFTWARE. org.codehaus.mojo animal-sniffer-annotations - 1.9 provided true org.jenkins-ci test-annotations - ${test-annotations.version} test @@ -680,25 +677,6 @@ THE SOFTWARE. org.apache.maven.plugins maven-enforcer-plugin - - - enforce - - - - - 1.8.0 - - - 3.0 - - - 1.${java.level} - - - - - enforce-banned-dependencies @@ -719,13 +697,7 @@ THE SOFTWARE. - - - org.codehaus.mojo - extra-enforcer-rules - 1.0-beta-6 - - + -- GitLab From b3e2ed8a37531d65e32d45070aaaae450e8af543 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 25 Aug 2017 14:28:24 -0400 Subject: [PATCH 0426/2185] [JENKINS-29537] Merged #2993: amended EnvironmentContributingAction signature. --- .../main/java/hudson/model/AbstractBuild.java | 2 +- .../model/EnvironmentContributingAction.java | 18 +++++++----------- .../java/hudson/model/ParametersAction.java | 2 +- core/src/main/java/hudson/model/Run.java | 7 +++++-- .../EnvironmentContributingActionTest.java | 16 ++++++---------- .../hudson/model/ParametersActionTest.java | 2 +- .../EnvironmentVariableNodePropertyTest.java | 2 +- 7 files changed, 22 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/hudson/model/AbstractBuild.java b/core/src/main/java/hudson/model/AbstractBuild.java index 2b7775c304..c8674fb028 100644 --- a/core/src/main/java/hudson/model/AbstractBuild.java +++ b/core/src/main/java/hudson/model/AbstractBuild.java @@ -887,7 +887,7 @@ public abstract class AbstractBuild

    ,R extends Abs e.buildEnvVars(env); for (EnvironmentContributingAction a : getActions(EnvironmentContributingAction.class)) - a.buildEnvVars(this,env,getBuiltOn()); + a.buildEnvVars(this,env); EnvVars.resolve(env); diff --git a/core/src/main/java/hudson/model/EnvironmentContributingAction.java b/core/src/main/java/hudson/model/EnvironmentContributingAction.java index 55f5035965..03ad23fb35 100644 --- a/core/src/main/java/hudson/model/EnvironmentContributingAction.java +++ b/core/src/main/java/hudson/model/EnvironmentContributingAction.java @@ -31,7 +31,6 @@ import hudson.tasks.BuildWrapper; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.ProtectedExternally; -import javax.annotation.CheckForNull; import javax.annotation.Nonnull; /** @@ -46,23 +45,20 @@ import javax.annotation.Nonnull; * * @author Kohsuke Kawaguchi * @since 1.318 - * @see AbstractBuild#getEnvironment(TaskListener) + * @see Run#getEnvironment(TaskListener) * @see BuildWrapper */ public interface EnvironmentContributingAction extends Action { /** - * Called by {@link Run} or {@link AbstractBuild} to allow plugins to contribute environment variables. + * Called by {@link Run} to allow plugins to contribute environment variables. * * @param run * The calling build. Never null. - * @param node - * The node execute on. Can be {@code null} when the Run is not binded to the node, - * e.g. in Pipeline outside the {@code node() step} * @param env * Environment variables should be added to this map. - * @since TODO + * @since 2.76 */ - default void buildEnvVars(@Nonnull Run run, @Nonnull EnvVars env, @CheckForNull Node node) { + default void buildEnvironment(@Nonnull Run run, @Nonnull EnvVars env) { if (run instanceof AbstractBuild && Util.isOverridden(EnvironmentContributingAction.class, getClass(), "buildEnvVars", AbstractBuild.class, EnvVars.class)) { @@ -73,7 +69,7 @@ public interface EnvironmentContributingAction extends Action { /** * Called by {@link AbstractBuild} to allow plugins to contribute environment variables. * - * @deprecated Use {@link #buildEnvVars(Run, EnvVars, Node)} instead + * @deprecated Use {@link #buildEnvironment} instead * * @param build * The calling build. Never null. @@ -84,8 +80,8 @@ public interface EnvironmentContributingAction extends Action { @Restricted(ProtectedExternally.class) default void buildEnvVars(AbstractBuild build, EnvVars env) { if (Util.isOverridden(EnvironmentContributingAction.class, - getClass(), "buildEnvVars", Run.class, EnvVars.class, Node.class)) { - buildEnvVars(build, env, build.getBuiltOn()); + getClass(), "buildEnvironment", Run.class, EnvVars.class)) { + buildEnvironment(build, env); } } } diff --git a/core/src/main/java/hudson/model/ParametersAction.java b/core/src/main/java/hudson/model/ParametersAction.java index 86e687b17f..60db6822d7 100644 --- a/core/src/main/java/hudson/model/ParametersAction.java +++ b/core/src/main/java/hudson/model/ParametersAction.java @@ -139,7 +139,7 @@ public class ParametersAction implements RunAction2, Iterable, Q } @Override - public void buildEnvVars(Run run, EnvVars env, Node node) { + public void buildEnvironment(Run run, EnvVars env) { for (ParameterValue p : getParameters()) { if (p == null) continue; p.buildEnvironment(run, env); diff --git a/core/src/main/java/hudson/model/Run.java b/core/src/main/java/hudson/model/Run.java index 46970882a2..9d9b7f4cd2 100644 --- a/core/src/main/java/hudson/model/Run.java +++ b/core/src/main/java/hudson/model/Run.java @@ -2301,8 +2301,11 @@ public abstract class Run ,RunT extends Run run, EnvVars env, @CheckForNull Node node) { + public void buildEnvironment(Run run, EnvVars env) { wasCalled = true; } @@ -50,7 +49,7 @@ public class EnvironmentContributingActionTest { } @Override - public void buildEnvVars(Run run, EnvVars env, @CheckForNull Node node) { + public void buildEnvironment(Run run, EnvVars env) { wasCalledRun = true; } @@ -71,7 +70,7 @@ public class EnvironmentContributingActionTest { Node node = mock(Node.class); OverrideRun overrideRun = new OverrideRun(); - overrideRun.buildEnvVars(run, envVars, node); + overrideRun.buildEnvironment(run, envVars); assertTrue(overrideRun.wasNewMethodCalled()); } @@ -99,10 +98,9 @@ public class EnvironmentContributingActionTest { @Test public void testOverrideAbstractBuildAndCallNewMethodWithAbstractBuild() throws Exception { AbstractBuild abstractBuild = mock(AbstractBuild.class); - Node node = mock(Node.class); OverrideAbstractBuild action = new OverrideAbstractBuild(); - action.buildEnvVars(abstractBuild, envVars, node); + action.buildEnvironment(abstractBuild, envVars); assertTrue(action.wasDeprecatedMethodCalled()); } @@ -114,10 +112,9 @@ public class EnvironmentContributingActionTest { @Test public void testOverrideAbstractBuildAndCallNewMethodWithRun() throws Exception { Run run = mock(Run.class); - Node node = mock(Node.class); OverrideAbstractBuild action = new OverrideAbstractBuild(); - action.buildEnvVars(run, envVars, node); + action.buildEnvironment(run, envVars); assertFalse(action.wasDeprecatedMethodCalled()); } @@ -139,10 +136,9 @@ public class EnvironmentContributingActionTest { @Test public void testOverrideBothAndCallNewMethod() throws Exception { Run run = mock(Run.class); - Node node = mock(Node.class); OverrideBoth overrideRun = new OverrideBoth(); - overrideRun.buildEnvVars(run, envVars, node); + overrideRun.buildEnvironment(run, envVars); assertTrue(overrideRun.wasRunCalled()); } diff --git a/core/src/test/java/hudson/model/ParametersActionTest.java b/core/src/test/java/hudson/model/ParametersActionTest.java index 182ead6ec1..6f7321ed19 100644 --- a/core/src/test/java/hudson/model/ParametersActionTest.java +++ b/core/src/test/java/hudson/model/ParametersActionTest.java @@ -105,7 +105,7 @@ public class ParametersActionTest { // Interaction with build EnvVars vars = new EnvVars(); - parametersAction.buildEnvVars(build, vars, build.getBuiltOn()); + parametersAction.buildEnvironment(build, vars); assertEquals(2, vars.size()); parametersAction.createVariableResolver(build); diff --git a/test/src/test/java/hudson/slaves/EnvironmentVariableNodePropertyTest.java b/test/src/test/java/hudson/slaves/EnvironmentVariableNodePropertyTest.java index b1a989ead9..e2ab922e8d 100644 --- a/test/src/test/java/hudson/slaves/EnvironmentVariableNodePropertyTest.java +++ b/test/src/test/java/hudson/slaves/EnvironmentVariableNodePropertyTest.java @@ -155,7 +155,7 @@ public class EnvironmentVariableNodePropertyTest extends HudsonTestCase { // use a timeout so we don't wait infinitely in case of failure FreeStyleBuild build = project.scheduleBuild2(0).get(/*10, TimeUnit.SECONDS*/); - System.out.println(build.getLog()); + System.out.println(build.getLog()); // TODO switch to BuildWatcher when converted to JenkinsRule assertEquals(Result.SUCCESS, build.getResult()); return builder.getEnvVars(); -- GitLab From dc8000cc1e36399595883858c3aae8f135177d49 Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Fri, 25 Aug 2017 22:34:27 +0200 Subject: [PATCH 0427/2185] Upgrade Remoting to 3.11 (#2988) * Use ClassFilter.appendDefaultFilter. * FindBugs * Update Jenkins Remoting to 3.11, fix reported FindBugs issues --- .../java/hudson/slaves/SlaveComputer.java | 6 +++++ core/src/main/java/jenkins/model/Jenkins.java | 25 +++---------------- pom.xml | 2 +- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java index a1ed233278..73fe5e17f6 100644 --- a/core/src/main/java/hudson/slaves/SlaveComputer.java +++ b/core/src/main/java/hudson/slaves/SlaveComputer.java @@ -454,6 +454,9 @@ public class SlaveComputer extends Computer { } @Override public Integer call() { Channel c = Channel.current(); + if (c == null) { + return -1; + } return resource ? c.resourceLoadingCount.get() : c.classLoadingCount.get(); } } @@ -471,6 +474,9 @@ public class SlaveComputer extends Computer { } @Override public Long call() { Channel c = Channel.current(); + if (c == null) { + return Long.valueOf(-1); + } return resource ? c.resourceLoadingTime.get() : c.classLoadingTime.get(); } } diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index f6a3d8386f..3719203219 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -251,7 +251,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; -import java.lang.reflect.Field; import java.net.BindException; import java.net.HttpURLConnection; import java.net.URL; @@ -903,26 +902,10 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve adjuncts = new AdjunctManager(servletContext, pluginManager.uberClassLoader,"adjuncts/"+SESSION_HASH, TimeUnit2.DAYS.toMillis(365)); - // TODO pending move to standard blacklist, or API to append filter - if (System.getProperty(ClassFilter.FILE_OVERRIDE_LOCATION_PROPERTY) == null) { // not using SystemProperties since ClassFilter does not either - try { - Field blacklistPatternsF = ClassFilter.DEFAULT.getClass().getDeclaredField("blacklistPatterns"); - blacklistPatternsF.setAccessible(true); - Object[] blacklistPatternsA = (Object[]) blacklistPatternsF.get(ClassFilter.DEFAULT); - boolean found = false; - for (int i = 0; i < blacklistPatternsA.length; i++) { - if (blacklistPatternsA[i] instanceof Pattern) { - blacklistPatternsA[i] = Pattern.compile("(" + blacklistPatternsA[i] + ")|(java[.]security[.]SignedObject)"); - found = true; - break; - } - } - if (!found) { - throw new Error("no Pattern found among " + Arrays.toString(blacklistPatternsA)); - } - } catch (NoSuchFieldException | IllegalAccessException x) { - throw new Error("Unexpected ClassFilter implementation in bundled remoting.jar: " + x, x); - } + try { + ClassFilter.appendDefaultFilter(Pattern.compile("java[.]security[.]SignedObject")); // TODO move to standard blacklist + } catch (ClassFilter.ClassFilterException ex) { + throw new IOException("Remoting library rejected the java[.]security[.]SignedObject blacklist pattern", ex); } // initialization consists of ... diff --git a/pom.xml b/pom.xml index 27c6c9cc8d..11eb771118 100644 --- a/pom.xml +++ b/pom.xml @@ -167,7 +167,7 @@ THE SOFTWARE. org.jenkins-ci.main remoting - 3.10 + 3.11 -- GitLab From 7b8ddc1973e6a986917c76f30d982d7e4c95b72f Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Sun, 27 Aug 2017 17:56:12 -0700 Subject: [PATCH 0428/2185] [maven-release-plugin] prepare release jenkins-2.76 --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index a8d38aa350..eac6dbba23 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main pom - 2.76-SNAPSHOT + 2.76 cli diff --git a/core/pom.xml b/core/pom.xml index 04e5c819a4..e348389f57 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.76-SNAPSHOT + 2.76 jenkins-core diff --git a/pom.xml b/pom.xml index 11eb771118..f2c7d0a16d 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.76-SNAPSHOT + 2.76 pom Jenkins main module @@ -58,7 +58,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - HEAD + jenkins-2.76 diff --git a/test/pom.xml b/test/pom.xml index d68cad1116..e30ba4ea1d 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.76-SNAPSHOT + 2.76 test diff --git a/war/pom.xml b/war/pom.xml index a9812aba46..2182adc82e 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.76-SNAPSHOT + 2.76 jenkins-war -- GitLab From 8189c2cd9a2cb0a4e6d2dcf341fb818dbd9165ba Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Sun, 27 Aug 2017 17:56:12 -0700 Subject: [PATCH 0429/2185] [maven-release-plugin] prepare for next development iteration --- cli/pom.xml | 2 +- core/pom.xml | 2 +- pom.xml | 4 ++-- test/pom.xml | 2 +- war/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/pom.xml b/cli/pom.xml index eac6dbba23..20c7e83213 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.main pom - 2.76 + 2.77-SNAPSHOT cli diff --git a/core/pom.xml b/core/pom.xml index e348389f57..066406da3a 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -29,7 +29,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.76 + 2.77-SNAPSHOT jenkins-core diff --git a/pom.xml b/pom.xml index f2c7d0a16d..a8f25c8d0e 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.76 + 2.77-SNAPSHOT pom Jenkins main module @@ -58,7 +58,7 @@ THE SOFTWARE. scm:git:git://github.com/jenkinsci/jenkins.git scm:git:ssh://git@github.com/jenkinsci/jenkins.git https://github.com/jenkinsci/jenkins - jenkins-2.76 + HEAD diff --git a/test/pom.xml b/test/pom.xml index e30ba4ea1d..b0a641d89f 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.76 + 2.77-SNAPSHOT test diff --git a/war/pom.xml b/war/pom.xml index 2182adc82e..9396b35ed7 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci.main pom - 2.76 + 2.77-SNAPSHOT jenkins-war -- GitLab From 6f537669d6f37150aaeeff6809c40e209d3c8020 Mon Sep 17 00:00:00 2001 From: istrangiu Date: Tue, 21 Mar 2017 14:24:24 +0000 Subject: [PATCH 0430/2185] JENKINS-42854: Added description field to the 'Computer' api --- core/src/main/java/hudson/model/Computer.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index afad020673..dec1b10d27 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -1067,6 +1067,17 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces return firstDemand; } + /** + * Returns the {@link Node} description for this computer + */ + @Restricted(DoNotUse.class) + @Exported + public @Nonnull String getDescription() { + Node node = getNode(); + return (node != null) ? node.getNodeDescription() : null; + } + + /** * Called by {@link Executor} to kill excessive executors from this computer. */ -- GitLab From 69828cd85ccfb6c0cb66609c80628ad64052bfcd Mon Sep 17 00:00:00 2001 From: Oleg Nenashev Date: Fri, 25 Aug 2017 22:34:27 +0200 Subject: [PATCH 0431/2185] Upgrade Remoting to 3.11 (#2988) * Use ClassFilter.appendDefaultFilter. * FindBugs * Update Jenkins Remoting to 3.11, fix reported FindBugs issues (cherry picked from commit dc8000cc1e36399595883858c3aae8f135177d49) --- .../java/hudson/slaves/SlaveComputer.java | 6 +++++ core/src/main/java/jenkins/model/Jenkins.java | 25 +++---------------- pom.xml | 2 +- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java index a1ed233278..73fe5e17f6 100644 --- a/core/src/main/java/hudson/slaves/SlaveComputer.java +++ b/core/src/main/java/hudson/slaves/SlaveComputer.java @@ -454,6 +454,9 @@ public class SlaveComputer extends Computer { } @Override public Integer call() { Channel c = Channel.current(); + if (c == null) { + return -1; + } return resource ? c.resourceLoadingCount.get() : c.classLoadingCount.get(); } } @@ -471,6 +474,9 @@ public class SlaveComputer extends Computer { } @Override public Long call() { Channel c = Channel.current(); + if (c == null) { + return Long.valueOf(-1); + } return resource ? c.resourceLoadingTime.get() : c.classLoadingTime.get(); } } diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index f6a3d8386f..3719203219 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -251,7 +251,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; -import java.lang.reflect.Field; import java.net.BindException; import java.net.HttpURLConnection; import java.net.URL; @@ -903,26 +902,10 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve adjuncts = new AdjunctManager(servletContext, pluginManager.uberClassLoader,"adjuncts/"+SESSION_HASH, TimeUnit2.DAYS.toMillis(365)); - // TODO pending move to standard blacklist, or API to append filter - if (System.getProperty(ClassFilter.FILE_OVERRIDE_LOCATION_PROPERTY) == null) { // not using SystemProperties since ClassFilter does not either - try { - Field blacklistPatternsF = ClassFilter.DEFAULT.getClass().getDeclaredField("blacklistPatterns"); - blacklistPatternsF.setAccessible(true); - Object[] blacklistPatternsA = (Object[]) blacklistPatternsF.get(ClassFilter.DEFAULT); - boolean found = false; - for (int i = 0; i < blacklistPatternsA.length; i++) { - if (blacklistPatternsA[i] instanceof Pattern) { - blacklistPatternsA[i] = Pattern.compile("(" + blacklistPatternsA[i] + ")|(java[.]security[.]SignedObject)"); - found = true; - break; - } - } - if (!found) { - throw new Error("no Pattern found among " + Arrays.toString(blacklistPatternsA)); - } - } catch (NoSuchFieldException | IllegalAccessException x) { - throw new Error("Unexpected ClassFilter implementation in bundled remoting.jar: " + x, x); - } + try { + ClassFilter.appendDefaultFilter(Pattern.compile("java[.]security[.]SignedObject")); // TODO move to standard blacklist + } catch (ClassFilter.ClassFilterException ex) { + throw new IOException("Remoting library rejected the java[.]security[.]SignedObject blacklist pattern", ex); } // initialization consists of ... diff --git a/pom.xml b/pom.xml index 740a6ec5a6..8ab404fade 100644 --- a/pom.xml +++ b/pom.xml @@ -168,7 +168,7 @@ THE SOFTWARE. org.jenkins-ci.main remoting - 3.10 + 3.11 -- GitLab From c709b1932c4a207db2463c147502fffe53e99018 Mon Sep 17 00:00:00 2001 From: "R. Tyler Croy" Date: Thu, 31 Aug 2017 10:34:49 -0700 Subject: [PATCH 0432/2185] Default the built-in Jenkins Update Center URL to https://updates.jenkins.io Now that we're using JDK8, we can rely on our Let's Encrypt-based certificates on *.jenkins.io Live from Jenkins World! Signed-off-by: M. Allan Signed-off-by: R. Tyler Croy --- core/src/main/java/hudson/model/DownloadService.java | 12 ------------ core/src/main/java/hudson/model/UpdateCenter.java | 2 +- core/src/main/java/hudson/model/UpdateSite.java | 12 ------------ .../src/test/java/hudson/model/UpdateCenterTest.java | 4 ++-- 4 files changed, 3 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/hudson/model/DownloadService.java b/core/src/main/java/hudson/model/DownloadService.java index 6c28c80d0f..bf67bbeb3c 100644 --- a/core/src/main/java/hudson/model/DownloadService.java +++ b/core/src/main/java/hudson/model/DownloadService.java @@ -132,18 +132,6 @@ public class DownloadService extends PageDecorator { } private String mapHttps(String url) { - /* - HACKISH: - - Loading scripts in HTTP from HTTPS pages cause browsers to issue a warning dialog. - The elegant way to solve the problem is to always load update center from HTTPS, - but our backend mirroring scheme isn't ready for that. So this hack serves regular - traffic in HTTP server, and only use HTTPS update center for Jenkins in HTTPS. - - We'll monitor the traffic to see if we can sustain this added traffic. - */ - if (url.startsWith("http://updates.jenkins-ci.org/") && Jenkins.getInstance().isRootUrlSecure()) - return "https"+url.substring(4); return url; } diff --git a/core/src/main/java/hudson/model/UpdateCenter.java b/core/src/main/java/hudson/model/UpdateCenter.java index cf6cb0d684..109efb9d37 100644 --- a/core/src/main/java/hudson/model/UpdateCenter.java +++ b/core/src/main/java/hudson/model/UpdateCenter.java @@ -148,7 +148,7 @@ import org.kohsuke.stapler.interceptor.RequirePOST; @ExportedBean public class UpdateCenter extends AbstractModelObject implements Saveable, OnMaster { - private static final String UPDATE_CENTER_URL = SystemProperties.getString(UpdateCenter.class.getName()+".updateCenterUrl","http://updates.jenkins-ci.org/"); + private static final String UPDATE_CENTER_URL = SystemProperties.getString(UpdateCenter.class.getName()+".updateCenterUrl","https://updates.jenkins.io/"); /** * Read timeout when downloading plugins, defaults to 1 minute diff --git a/core/src/main/java/hudson/model/UpdateSite.java b/core/src/main/java/hudson/model/UpdateSite.java index ddf399ceca..933cfe8e83 100644 --- a/core/src/main/java/hudson/model/UpdateSite.java +++ b/core/src/main/java/hudson/model/UpdateSite.java @@ -485,18 +485,6 @@ public class UpdateSite { */ @Deprecated public String getDownloadUrl() { - /* - HACKISH: - - Loading scripts in HTTP from HTTPS pages cause browsers to issue a warning dialog. - The elegant way to solve the problem is to always load update center from HTTPS, - but our backend mirroring scheme isn't ready for that. So this hack serves regular - traffic in HTTP server, and only use HTTPS update center for Jenkins in HTTPS. - - We'll monitor the traffic to see if we can sustain this added traffic. - */ - if (url.equals("http://updates.jenkins-ci.org/update-center.json") && Jenkins.getInstance().isRootUrlSecure()) - return "https"+url.substring(4); return url; } diff --git a/test/src/test/java/hudson/model/UpdateCenterTest.java b/test/src/test/java/hudson/model/UpdateCenterTest.java index c073e4206b..29e70cbb64 100644 --- a/test/src/test/java/hudson/model/UpdateCenterTest.java +++ b/test/src/test/java/hudson/model/UpdateCenterTest.java @@ -44,8 +44,8 @@ import org.junit.Test; public class UpdateCenterTest { @Test public void data() throws Exception { try { - doData("http://updates.jenkins-ci.org/update-center.json?version=build"); - doData("http://updates.jenkins-ci.org/stable/update-center.json?version=build"); + doData("https://updates.jenkins.io/update-center.json?version=build"); + doData("https://updates.jenkins.io/stable/update-center.json?version=build"); } catch (Exception x) { // TODO this should not be in core at all; should be in repo built by a separate job somewhere assumeNoException("Might be no Internet connectivity, or might start failing due to expiring certificate through no fault of code changes", x); -- GitLab From 08a07fc69ece6e1be23d72e5116e06aa02e18a3e Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Sat, 2 Sep 2017 02:30:20 +0200 Subject: [PATCH 0433/2185] [JENKINS-46603] Verify https://github.com/jenkinsci/pom/pull/16 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a8f25c8d0e..2cabdb9811 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ THE SOFTWARE. org.jenkins-ci jenkins - 1.38 + 1.39-20170902.001419-2 org.jenkins-ci.main -- GitLab From f420038bba05e66b21565348c5595e1f32c35983 Mon Sep 17 00:00:00 2001 From: Baptiste Mathus Date: Sat, 2 Sep 2017 02:58:12 +0200 Subject: [PATCH 0434/2185] [JENKINS-46603] Remove overrides to inherit upgraded versions --- pom.xml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/pom.xml b/pom.xml index 2cabdb9811..69ac65d75b 100644 --- a/pom.xml +++ b/pom.xml @@ -347,12 +347,10 @@ THE SOFTWARE. org.apache.maven.plugins maven-dependency-plugin - 2.8 org.apache.maven.plugins maven-compiler-plugin - 3.0 true alwaysNew @@ -361,17 +359,14 @@ THE SOFTWARE. org.apache.maven.plugins maven-gpg-plugin - 1.4 org.apache.maven.plugins maven-install-plugin - 2.3.1 org.apache.maven.plugins maven-javadoc-plugin - 2.10.3 true @@ -379,17 +374,14 @@ THE SOFTWARE. org.apache.maven.plugins maven-jar-plugin - 2.6 org.apache.maven.plugins maven-war-plugin - 2.6 org.apache.maven.plugins maven-surefire-plugin - 2.20 -noverify @@ -403,7 +395,6 @@ THE SOFTWARE. org.apache.maven.plugins maven-assembly-plugin - 2.4 maven-jarsigner-plugin @@ -423,7 +414,6 @@ THE SOFTWARE. org.apache.maven.plugins maven-resources-plugin - 2.6 @@ -541,12 +527,10 @@ THE SOFTWARE. org.jenkins-ci.tools maven-hpi-plugin - 2.0 org.apache.maven.plugins maven-site-plugin - 3.3 org.kohsuke @@ -558,7 +542,6 @@ THE SOFTWARE. org.apache.maven.plugins maven-enforcer-plugin - 3.0.0-M1 @@ -603,7 +586,6 @@ THE SOFTWARE. org.codehaus.mojo animal-sniffer-maven-plugin - 1.15 @@ -615,7 +597,6 @@ THE SOFTWARE. maven-release-plugin - 2.5.1 -P release,sign -- GitLab From 0efdf8fb4f8c56f1f32fb390c472cb2e98e67f56 Mon Sep 17 00:00:00 2001 From: hplatou Date: Sat, 2 Sep 2017 21:08:11 +0200 Subject: [PATCH 0435/2185] [JENKINS-13153] - Use directory from env:BASE when writing jenkins.copies (#2992) [JENKINS-13153] - Use directory from env:BASE when writing jenkins.copies --- .../lifecycle/WindowsServiceLifecycle.java | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/hudson/lifecycle/WindowsServiceLifecycle.java b/core/src/main/java/hudson/lifecycle/WindowsServiceLifecycle.java index 94066417b0..42519eb21b 100644 --- a/core/src/main/java/hudson/lifecycle/WindowsServiceLifecycle.java +++ b/core/src/main/java/hudson/lifecycle/WindowsServiceLifecycle.java @@ -60,20 +60,20 @@ public class WindowsServiceLifecycle extends Lifecycle { */ private void updateJenkinsExeIfNeeded() { try { - File rootDir = Jenkins.getInstance().getRootDir(); + File baseDir = getBaseDir(); URL exe = getClass().getResource("/windows-service/jenkins.exe"); String ourCopy = Util.getDigestOf(exe.openStream()); for (String name : new String[]{"hudson.exe","jenkins.exe"}) { try { - File currentCopy = new File(rootDir,name); + File currentCopy = new File(baseDir,name); if(!currentCopy.exists()) continue; String curCopy = new FilePath(currentCopy).digest(); if(ourCopy.equals(curCopy)) continue; // identical - File stage = new File(rootDir,name+".new"); + File stage = new File(baseDir,name+".new"); FileUtils.copyURLToFile(exe,stage); Kernel32.INSTANCE.MoveFileExA(stage.getAbsolutePath(),currentCopy.getAbsolutePath(),MOVEFILE_DELAY_UNTIL_REBOOT|MOVEFILE_REPLACE_EXISTING); LOGGER.info("Scheduled a replacement of "+name); @@ -107,8 +107,8 @@ public class WindowsServiceLifecycle extends Lifecycle { String baseName = dest.getName(); baseName = baseName.substring(0,baseName.indexOf('.')); - File rootDir = Jenkins.getInstance().getRootDir(); - File copyFiles = new File(rootDir,baseName+".copies"); + File baseDir = getBaseDir(); + File copyFiles = new File(baseDir,baseName+".copies"); try (FileWriter w = new FileWriter(copyFiles, true)) { w.write(by.getAbsolutePath() + '>' + getHudsonWar().getAbsolutePath() + '\n'); @@ -144,6 +144,19 @@ public class WindowsServiceLifecycle extends Lifecycle { if(r!=0) throw new IOException(baos.toString()); } + + private static final File getBaseDir() { + File baseDir; + + String baseEnv = System.getenv("BASE"); + if (baseEnv != null) { + baseDir = new File(baseEnv); + } else { + LOGGER.log(Level.WARNING, "Could not find environment variable 'BASE' for Jenkins base directory. Falling back to JENKINS_HOME"); + baseDir = Jenkins.getInstance().getRootDir(); + } + return baseDir; + } private static final Logger LOGGER = Logger.getLogger(WindowsServiceLifecycle.class.getName()); } -- GitLab From 30a927fd8c1cb6e38ded402e1ba1614c3dffbba5 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Sat, 2 Sep 2017 14:14:38 -0700 Subject: [PATCH 0436/2185] rely on java8 default methods to avoid code duplication Signed-off-by: Nicolas De Loof --- core/src/main/java/hudson/model/AbstractItem.java | 14 -------------- core/src/main/java/hudson/model/Computer.java | 8 -------- .../main/java/hudson/model/MyViewsProperty.java | 8 -------- core/src/main/java/hudson/model/Node.java | 8 -------- core/src/main/java/hudson/model/Run.java | 10 ---------- core/src/main/java/hudson/model/User.java | 8 -------- core/src/main/java/hudson/model/View.java | 8 -------- .../java/hudson/security/AccessControlled.java | 8 ++++++-- core/src/main/java/hudson/slaves/Cloud.java | 8 -------- 9 files changed, 6 insertions(+), 74 deletions(-) diff --git a/core/src/main/java/hudson/model/AbstractItem.java b/core/src/main/java/hudson/model/AbstractItem.java index 424e934e31..5fd538151a 100644 --- a/core/src/main/java/hudson/model/AbstractItem.java +++ b/core/src/main/java/hudson/model/AbstractItem.java @@ -492,20 +492,6 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet return Jenkins.getInstance().getAuthorizationStrategy().getACL(this); } - /** - * Short for {@code getACL().checkPermission(p)} - */ - public void checkPermission(Permission p) { - getACL().checkPermission(p); - } - - /** - * Short for {@code getACL().hasPermission(p)} - */ - public boolean hasPermission(Permission p) { - return getACL().hasPermission(p); - } - /** * Save the settings to a file. */ diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index afad020673..db4ecf3d21 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -332,14 +332,6 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces return Jenkins.getInstance().getAuthorizationStrategy().getACL(this); } - public void checkPermission(Permission permission) { - getACL().checkPermission(permission); - } - - public boolean hasPermission(Permission permission) { - return getACL().hasPermission(permission); - } - /** * If the computer was offline (either temporarily or not), * this method will return the cause. diff --git a/core/src/main/java/hudson/model/MyViewsProperty.java b/core/src/main/java/hudson/model/MyViewsProperty.java index 8e823efd5a..68ff6e2513 100644 --- a/core/src/main/java/hudson/model/MyViewsProperty.java +++ b/core/src/main/java/hudson/model/MyViewsProperty.java @@ -185,14 +185,6 @@ public class MyViewsProperty extends UserProperty implements ModifiableViewGroup return user.getACL(); } - public void checkPermission(Permission permission) throws AccessDeniedException { - getACL().checkPermission(permission); - } - - public boolean hasPermission(Permission permission) { - return getACL().hasPermission(permission); - } - ///// Action methods ///// public String getDisplayName() { return Messages.MyViewsProperty_DisplayName(); diff --git a/core/src/main/java/hudson/model/Node.java b/core/src/main/java/hudson/model/Node.java index ab1ca6ca66..89a7dd0025 100644 --- a/core/src/main/java/hudson/model/Node.java +++ b/core/src/main/java/hudson/model/Node.java @@ -509,14 +509,6 @@ public abstract class Node extends AbstractModelObject implements Reconfigurable return Jenkins.getInstance().getAuthorizationStrategy().getACL(this); } - public final void checkPermission(Permission permission) { - getACL().checkPermission(permission); - } - - public final boolean hasPermission(Permission permission) { - return getACL().hasPermission(permission); - } - public Node reconfigure(final StaplerRequest req, JSONObject form) throws FormException { if (form==null) return null; diff --git a/core/src/main/java/hudson/model/Run.java b/core/src/main/java/hudson/model/Run.java index 9d9b7f4cd2..bbf0a73c77 100644 --- a/core/src/main/java/hudson/model/Run.java +++ b/core/src/main/java/hudson/model/Run.java @@ -1456,16 +1456,6 @@ public abstract class Run ,RunT extends Run Date: Sat, 2 Sep 2017 21:42:04 -0400 Subject: [PATCH 0437/2185] [JENKINS-45892] Enhanced diagnostics (#2997) * [JENKINS-45892] Enhanced diagnostics. * Refined fix which should avoid a needless warning when called from MultiBranchProject.onLoad. --- core/src/main/java/hudson/XmlFile.java | 9 ++++++--- test/src/test/java/hudson/model/AbstractItem2Test.java | 8 ++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/hudson/XmlFile.java b/core/src/main/java/hudson/XmlFile.java index 3c0a68bee3..d5077b34db 100644 --- a/core/src/main/java/hudson/XmlFile.java +++ b/core/src/main/java/hudson/XmlFile.java @@ -120,6 +120,7 @@ public final class XmlFile { private final XStream xs; private final File file; private static final Map beingWritten = Collections.synchronizedMap(new IdentityHashMap<>()); + private static final ThreadLocal writing = new ThreadLocal<>(); public XmlFile(File file) { this(DEFAULT_XSTREAM,file); @@ -175,10 +176,12 @@ public final class XmlFile { try { w.write("\n"); beingWritten.put(o, null); + writing.set(file); try { xs.toXML(o, w); } finally { beingWritten.remove(o); + writing.set(null); } w.commit(); } catch(StreamException e) { @@ -200,11 +203,11 @@ public final class XmlFile { * @since 2.74 */ public static Object replaceIfNotAtTopLevel(Object o, Supplier replacement) { - if (beingWritten.containsKey(o)) { + File currentlyWriting = writing.get(); + if (beingWritten.containsKey(o) || currentlyWriting == null) { return o; } else { - // Unfortunately we cannot easily tell which XML file is actually being saved here, at least without implementing a custom Converter. - LOGGER.log(Level.WARNING, "JENKINS-45892: reference to {0} being saved but not at top level", o); + LOGGER.log(Level.WARNING, "JENKINS-45892: reference to " + o + " being saved from unexpected " + currentlyWriting, new IllegalStateException()); return replacement.get(); } } diff --git a/test/src/test/java/hudson/model/AbstractItem2Test.java b/test/src/test/java/hudson/model/AbstractItem2Test.java index a35044ee87..968dc5f54f 100644 --- a/test/src/test/java/hudson/model/AbstractItem2Test.java +++ b/test/src/test/java/hudson/model/AbstractItem2Test.java @@ -23,6 +23,8 @@ */ package hudson.model; +import hudson.XmlFile; +import java.util.logging.Level; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.not; import org.junit.Test; @@ -30,6 +32,7 @@ import static org.junit.Assert.*; import org.junit.Rule; import org.junit.runners.model.Statement; import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.LoggerRule; import org.jvnet.hudson.test.RestartableJenkinsRule; public class AbstractItem2Test { @@ -37,6 +40,9 @@ public class AbstractItem2Test { @Rule public RestartableJenkinsRule rr = new RestartableJenkinsRule(); + @Rule + public LoggerRule logging = new LoggerRule().record(XmlFile.class, Level.WARNING).capture(100); + @Issue("JENKINS-45892") @Test public void badSerialization() { @@ -50,6 +56,8 @@ public class AbstractItem2Test { String text = p2.getConfigFile().asString(); assertThat(text, not(containsString("this is p1"))); assertThat(text, containsString("p1")); + assertThat(logging.getMessages().toString(), containsString(p1.toString())); + assertThat(logging.getMessages().toString(), containsString(p2.getConfigFile().toString())); } }); rr.addStep(new Statement() { -- GitLab From 3bc9c86556422414bd90e36ac930af209752afe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20Gr=C3=BCnewaldt?= Date: Sun, 3 Sep 2017 03:50:17 +0200 Subject: [PATCH 0438/2185] Rss Bar and Legend Link (Job List Footer) Added Classes and IDs to enable easy styling for external themes (#2989) * ui classes to enable easy styling * fix align right html to css * move css to style.css * Revert "move css to style.css" This reverts commit f26162a0f350886040935811d6194d585f8a1bf9. * move css to style.css (without unrelated spaces changed) * remove ids and use classes --- .../main/resources/lib/hudson/rssBar.jelly | 22 +++++++++---------- war/src/main/webapp/css/style.css | 16 ++++++++++++++ 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/core/src/main/resources/lib/hudson/rssBar.jelly b/core/src/main/resources/lib/hudson/rssBar.jelly index 942beae5d6..4db12af6c9 100644 --- a/core/src/main/resources/lib/hudson/rssBar.jelly +++ b/core/src/main/resources/lib/hudson/rssBar.jelly @@ -24,22 +24,22 @@ THE SOFTWARE. -
    - ${%Legend} - - Feed + diff --git a/war/src/main/webapp/css/style.css b/war/src/main/webapp/css/style.css index f51ad90673..0bc3d00dca 100644 --- a/war/src/main/webapp/css/style.css +++ b/war/src/main/webapp/css/style.css @@ -1913,3 +1913,19 @@ body.no-sticker #bottom-sticker { width: 48px; height: 48px; } + +/* rss-bar */ + +#rss-bar { + margin:1em; + text-align:right; +} + +#rss-bar .icon-rss { + border: 0; +} + +#rss-bar .rss-bar-item { + padding-left: 1em; +} + -- GitLab From 33799df36cbf2f5e0c5d0ac8372ff761e82c3784 Mon Sep 17 00:00:00 2001 From: Josiah Haswell Date: Sat, 2 Sep 2017 19:58:15 -0600 Subject: [PATCH 0439/2185] [FIXED JENKINS-31068] Monitor does not detect when Tomcat URL encoding parameter rejects forward slashes in URL (#2977) * Fixing JENKINS-31068 * backing out changes--they don't fully work * Saving progress so that I can revert to an earlier version for tests * So, pretty exhaustive testing yields that these modifications have the same behavior as the previous versions * [FIX JENKINS-31068] Adding wiki reference to error message. Adding trailing slash to URL * [FIX JENKINS-31068] It looks like different versions of Tomcat and Apache HTTP handle this case differently. Really, the best we can do is check to see if the test method was not hit and passed correctly--if we hit it, we get more information on the configuration error. If we don't, we just refer them to a general wiki page --- .../hudson/diagnosis/ReverseProxySetupMonitor/message.jelly | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message.jelly b/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message.jelly index a39c1238b0..a92382aa46 100644 --- a/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message.jelly +++ b/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message.jelly @@ -27,7 +27,9 @@ THE SOFTWARE. -