mirror of
https://github.com/jenkinsci/jenkins.git
synced 2026-11-15 13:02:19 +00:00
Merge branch 'stable-2.121' of git://github.com/jenkinsci/jenkins into stable-2.121
This commit is contained in:
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());
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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 {}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user