Merge branch 'stable-2.121' of git://github.com/jenkinsci/jenkins into stable-2.121

This commit is contained in:
Daniel Beck committed 2018-06-21 01:47:19 +02:00
commit f2aea1563f
10 files changed
+175 -25

No files matched your search

@@ -25,6 +25,8 @@ package hudson;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
@@ -110,11 +112,20 @@ public class ClassicPluginStrategy implements PluginStrategy {
@Override public String getShortName(File archive) throws IOException {
Manifest manifest;
if (!archive.exists()) {
throw new FileNotFoundException("Failed to load " + archive + ". The file does not exist");
} else if (!archive.isFile()) {
throw new FileNotFoundException("Failed to load " + archive + ". It is not a file");
}
if (isLinked(archive)) {
manifest = loadLinkedManifest(archive);
} else {
try (JarFile jf = new JarFile(archive, false)) {
manifest = jf.getManifest();
} catch (IOException ex) {
// Mention file name in the exception
throw new IOException("Failed to load " + archive, ex);
}
}
return PluginWrapper.computeShortName(manifest, archive.getName());
+4
View File
@@ -1554,6 +1554,10 @@ public abstract class Run <JobT extends Job<JobT,RunT>,RunT extends Run<JobT,Run
RunListener.fireDeleted(this);
if (artifactManager != null) {
deleteArtifacts();
} // for StandardArtifactManager, deleting the whole build dir suffices
synchronized (this) { // avoid holding a lock while calling plugin impls of onDeleted
File tmp = new File(rootDir.getParentFile(),'.'+rootDir.getName());
@@ -39,6 +39,7 @@ import hudson.model.User;
import hudson.remoting.Channel;
import hudson.remoting.ChannelBuilder;
import hudson.remoting.ChannelClosedException;
import hudson.remoting.CommandTransport;
import hudson.remoting.Launcher;
import hudson.remoting.VirtualChannel;
import hudson.security.ACL;
@@ -60,6 +61,8 @@ import jenkins.slaves.systemInfo.SlaveSystemInfo;
import jenkins.util.SystemProperties;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.Beta;
import org.kohsuke.stapler.HttpRedirect;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.QueryParameter;
@@ -69,6 +72,7 @@ import org.kohsuke.stapler.WebMethod;
import org.kohsuke.stapler.interceptor.RequirePOST;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.OverridingMethodsMustInvokeSuper;
import javax.servlet.ServletException;
import java.io.File;
@@ -380,7 +384,15 @@ public class SlaveComputer extends Computer {
private final Object channelLock = new Object();
public void setChannel(InputStream in, OutputStream out, TaskListener taskListener, Channel.Listener listener) throws IOException, InterruptedException {
/**
* Creates a {@link Channel} from the given stream and sets that to this agent.
*
* Same as {@link #setChannel(InputStream, OutputStream, OutputStream, Channel.Listener)}, but for
* {@link TaskListener}.
*/
public void setChannel(@Nonnull InputStream in, @Nonnull OutputStream out,
@Nonnull TaskListener taskListener,
@CheckForNull Channel.Listener listener) throws IOException, InterruptedException {
setChannel(in,out,taskListener.getLogger(),listener);
}
@@ -403,7 +415,9 @@ public class SlaveComputer extends Computer {
* By the time this method is called, the cause of the termination is reported to the user,
* so the implementation of the listener doesn't need to do that again.
*/
public void setChannel(InputStream in, OutputStream out, OutputStream launchLog, Channel.Listener listener) throws IOException, InterruptedException {
public void setChannel(@Nonnull InputStream in, @Nonnull OutputStream out,
@CheckForNull OutputStream launchLog,
@CheckForNull Channel.Listener listener) throws IOException, InterruptedException {
ChannelBuilder cb = new ChannelBuilder(nodeName,threadPoolForRemoting)
.withMode(Channel.Mode.NEGOTIATE)
.withHeaderStream(launchLog);
@@ -416,6 +430,39 @@ public class SlaveComputer extends Computer {
setChannel(channel,launchLog,listener);
}
/**
* Creates a {@link Channel} from the given Channel Builder and Command Transport.
* This method can be used to allow {@link ComputerLauncher}s to create channels not based on I/O streams.
*
* @param cb
* Channel Builder.
* To print launch logs this channel builder should have a Header Stream defined
* (see {@link ChannelBuilder#getHeaderStream()}) in this argument or by one of {@link ChannelConfigurator}s.
* @param commandTransport
* Command Transport
* @param listener
* Gets a notification when the channel closes, to perform clean up. Can be {@code null}.
* By the time this method is called, the cause of the termination is reported to the user,
* so the implementation of the listener doesn't need to do that again.
* @since 2.127
*/
@Restricted(Beta.class)
public void setChannel(@Nonnull ChannelBuilder cb,
@Nonnull CommandTransport commandTransport,
@CheckForNull Channel.Listener listener) throws IOException, InterruptedException {
for (ChannelConfigurator cc : ChannelConfigurator.all()) {
cc.onChannelBuilding(cb,this);
}
OutputStream headerStream = cb.getHeaderStream();
if (headerStream == null) {
LOGGER.log(Level.WARNING, "No header stream defined when setting channel for computer {0}. " +
"Launch log won't be printed", this);
}
Channel channel = cb.build(commandTransport);
setChannel(channel, headerStream, listener);
}
/**
* Shows {@link Channel#classLoadingCount}.
* @since 1.495
@@ -509,14 +556,18 @@ public class SlaveComputer extends Computer {
/**
* Sets up the connection through an existing channel.
* @param channel the channel to use; <strong>warning:</strong> callers are expected to have called {@link ChannelConfigurator} already
* @param channel the channel to use; <strong>warning:</strong> callers are expected to have called {@link ChannelConfigurator} already.
* @param launchLog Launch log. If not {@code null}, will receive launch log messages
* @param listener Channel event listener to be attached (if not {@code null})
* @since 1.444
*/
public void setChannel(Channel channel, OutputStream launchLog, Channel.Listener listener) throws IOException, InterruptedException {
public void setChannel(@Nonnull Channel channel,
@CheckForNull OutputStream launchLog,
@CheckForNull Channel.Listener listener) throws IOException, InterruptedException {
if(this.channel!=null)
throw new IllegalStateException("Already connected");
final TaskListener taskListener = new StreamTaskListener(launchLog);
final TaskListener taskListener = launchLog != null ? new StreamTaskListener(launchLog) : TaskListener.NULL;
PrintStream log = taskListener.getLogger();
channel.setProperty(SlaveComputer.class, this);
@@ -23,6 +23,7 @@
*/
package hudson.util;
import com.google.common.annotations.Beta;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
@@ -35,6 +36,9 @@ import java.util.AbstractList;
import java.util.Arrays;
import java.util.Set;
import java.util.HashSet;
import javax.annotation.Nonnull;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
/**
* Varios {@link Iterator} implementations.
@@ -403,4 +407,20 @@ public class Iterators {
public interface CountingPredicate<T> {
boolean apply(int index, T input);
}
/**
* Similar to {@link com.google.common.collect.Iterators#skip} except not {@link Beta}.
* @param iterator some iterator
* @param count a nonnegative count
*/
@Restricted(NoExternalUse.class)
public static void skip(@Nonnull Iterator<?> iterator, int count) {
if (count < 0) {
throw new IllegalArgumentException();
}
while (iterator.hasNext() && count-- > 0) {
iterator.next();
}
}
}
+1 -1
View File
@@ -150,7 +150,7 @@ public class RunList<R extends Run> extends AbstractList<R> {
public List<R> subList(int fromIndex, int toIndex) {
List<R> r = new ArrayList<R>();
Iterator<R> itr = iterator();
Iterators.skip(itr,fromIndex);
hudson.util.Iterators.skip(itr, fromIndex);
for (int i=toIndex-fromIndex; i>0; i--) {
r.add(itr.next());
}
@@ -44,6 +44,9 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import org.kohsuke.stapler.framework.io.WriterOutputStream;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
// TODO: AbstractTaskListener is empty now, but there are dependencies on that e.g. Ruby Runtime - JENKINS-48116)
// The change needs API deprecation policy or external usages cleanup.
@@ -56,7 +59,9 @@ import org.kohsuke.stapler.framework.io.WriterOutputStream;
* @author Kohsuke Kawaguchi
*/
public class StreamTaskListener extends AbstractTaskListener implements TaskListener, Closeable {
@Nonnull
private PrintStream out;
@CheckForNull
private Charset charset;
/**
@@ -66,15 +71,15 @@ public class StreamTaskListener extends AbstractTaskListener implements TaskList
* or use {@link #fromStdout()} or {@link #fromStderr()}.
*/
@Deprecated
public StreamTaskListener(PrintStream out) {
public StreamTaskListener(@Nonnull PrintStream out) {
this(out,null);
}
public StreamTaskListener(OutputStream out) {
public StreamTaskListener(@Nonnull OutputStream out) {
this(out,null);
}
public StreamTaskListener(OutputStream out, Charset charset) {
public StreamTaskListener(@Nonnull OutputStream out, @CheckForNull Charset charset) {
try {
if (charset == null)
this.out = (out instanceof PrintStream) ? (PrintStream)out : new PrintStream(out, false);
@@ -87,18 +92,18 @@ public class StreamTaskListener extends AbstractTaskListener implements TaskList
}
}
public StreamTaskListener(File out) throws IOException {
public StreamTaskListener(@Nonnull File out) throws IOException {
this(out,null);
}
public StreamTaskListener(File out, Charset charset) throws IOException {
public StreamTaskListener(@Nonnull File out, @CheckForNull Charset charset) throws IOException {
// don't do buffering so that what's written to the listener
// gets reflected to the file immediately, which can then be
// served to the browser immediately
this(Files.newOutputStream(asPath(out)), charset);
}
private static Path asPath(File out) throws IOException {
private static Path asPath(@Nonnull File out) throws IOException {
try {
return out.toPath();
} catch (InvalidPathException e) {
@@ -115,7 +120,7 @@ public class StreamTaskListener extends AbstractTaskListener implements TaskList
* @throws IOException if the file could not be opened.
* @since 1.651
*/
public StreamTaskListener(File out, boolean append, Charset charset) throws IOException {
public StreamTaskListener(@Nonnull File out, boolean append, @CheckForNull Charset charset) throws IOException {
// don't do buffering so that what's written to the listener
// gets reflected to the file immediately, which can then be
// served to the browser immediately
@@ -127,7 +132,7 @@ public class StreamTaskListener extends AbstractTaskListener implements TaskList
);
}
public StreamTaskListener(Writer w) throws IOException {
public StreamTaskListener(@Nonnull Writer w) throws IOException {
this(new WriterOutputStream(w));
}
@@ -155,7 +160,7 @@ public class StreamTaskListener extends AbstractTaskListener implements TaskList
@Override
public Charset getCharset() {
return charset;
return charset != null ? charset : Charset.defaultCharset();
}
private void writeObject(ObjectOutputStream out) throws IOException {
@@ -24,7 +24,6 @@
package jenkins.widgets;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import hudson.model.AbstractBuild;
import hudson.model.Job;
import hudson.model.ParameterValue;
@@ -32,6 +31,7 @@ import hudson.model.ParametersAction;
import hudson.model.Queue;
import hudson.model.Run;
import hudson.search.UserSearchProperty;
import hudson.util.Iterators;
import hudson.widgets.HistoryWidget;
import javax.annotation.Nonnull;
@@ -32,6 +32,7 @@ import java.util.Iterator;
import java.util.List;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
/**
* @author Kohsuke Kawaguchi
@@ -77,9 +78,27 @@ public class IteratorsTest {
assertEquals("[]", com.google.common.collect.Iterators.toString(Iterators.limit(asList(1,2,4,6).iterator(), EVEN)));
}
public static final CountingPredicate<Integer> EVEN = new CountingPredicate<Integer>() {
public boolean apply(int index, Integer input) {
return input % 2 == 0;
}
};
public static final CountingPredicate<Integer> EVEN = (index, input) -> input % 2 == 0;
@Issue("JENKINS-51779")
@Test
public void skip() {
List<Integer> lst = Iterators.sequence(1, 4);
Iterator<Integer> it = lst.iterator();
Iterators.skip(it, 0);
assertEquals("[1, 2, 3]", com.google.common.collect.Iterators.toString(it));
it = lst.iterator();
Iterators.skip(it, 1);
assertEquals("[2, 3]", com.google.common.collect.Iterators.toString(it));
it = lst.iterator();
Iterators.skip(it, 2);
assertEquals("[3]", com.google.common.collect.Iterators.toString(it));
it = lst.iterator();
Iterators.skip(it, 3);
assertEquals("[]", com.google.common.collect.Iterators.toString(it));
it = lst.iterator();
Iterators.skip(it, 4);
assertEquals("[]", com.google.common.collect.Iterators.toString(it));
}
}
+1 -1
View File
@@ -107,7 +107,7 @@ THE SOFTWARE.
<maven-war-plugin.version>3.0.0</maven-war-plugin.version> <!-- JENKINS-47127 bump when 3.2.0 is out. Cf. MWAR-407 -->
<!-- Bundled Remoting version -->
<remoting.version>3.20</remoting.version>
<remoting.version>3.21</remoting.version>
<!-- Minimum Remoting version, which is tested for API compatibility -->
<remoting.minimum.supported.version>2.60</remoting.minimum.supported.version>
+43 -3
View File
@@ -23,19 +23,30 @@
*/
package hudson.model;
import hudson.FilePath;
import hudson.Launcher;
import hudson.tasks.ArtifactArchiver;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import jenkins.model.ArtifactManager;
import jenkins.model.ArtifactManagerConfiguration;
import jenkins.model.ArtifactManagerFactory;
import jenkins.model.ArtifactManagerFactoryDescriptor;
import jenkins.model.Jenkins;
import jenkins.util.VirtualFile;
import static 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.jvnet.hudson.test.TestExtension;
import org.kohsuke.stapler.DataBoundConstructor;
/**
* @author Kohsuke Kawaguchi
*/
public class RunTest {
@Rule public JenkinsRule j = new JenkinsRule();
@@ -73,4 +84,33 @@ public class RunTest {
assertEquals(Run.KeepLogBuildBadge.class, badgeActions.get(0).getClass());
}
@Issue("JENKINS-51819")
@Test public void deleteArtifactsCustom() throws Exception {
ArtifactManagerConfiguration.get().getArtifactManagerFactories().add(new Mgr.Factory());
FreeStyleProject p = j.createFreeStyleProject();
j.jenkins.getWorkspaceFor(p).child("f").write("", null);
p.getPublishersList().add(new ArtifactArchiver("f"));
FreeStyleBuild b = j.buildAndAssertSuccess(p);
b.delete();
assertTrue(Mgr.deleted.get());
}
private static final class Mgr extends ArtifactManager {
static final AtomicBoolean deleted = new AtomicBoolean();
@Override public boolean delete() throws IOException, InterruptedException {
return !deleted.getAndSet(true);
}
@Override public void onLoad(Run<?, ?> build) {}
@Override public void archive(FilePath workspace, Launcher launcher, BuildListener listener, Map<String, String> artifacts) throws IOException, InterruptedException {}
@Override public VirtualFile root() {
return VirtualFile.forFile(Jenkins.get().getRootDir()); // irrelevant
}
public static final class Factory extends ArtifactManagerFactory {
@DataBoundConstructor public Factory() {}
@Override public ArtifactManager managerFor(Run<?, ?> build) {
return new Mgr();
}
@TestExtension("deleteArtifactsCustom") public static final class DescriptorImpl extends ArtifactManagerFactoryDescriptor {}
}
}
}