From 1fc3edbd5824b6ae650bb94d212e2d2ddc245c36 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Mon, 8 Sep 2014 12:36:50 +0100 Subject: [PATCH 01/54] Use SECURITY-144 branch of remoting as the starting point --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f4fd21221f..4192f84731 100644 --- a/pom.xml +++ b/pom.xml @@ -173,7 +173,7 @@ THE SOFTWARE. org.jenkins-ci.main remoting - 2.32 + 2.40-SNAPSHOT From 9009cccffb0ba1dab372f1c777b29616a458f08a Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Mon, 8 Sep 2014 12:44:32 +0100 Subject: [PATCH 02/54] Updating to reflect changes made in remoting --- core/src/main/java/hudson/Launcher.java | 2 +- core/src/main/java/hudson/slaves/Channels.java | 4 ++-- pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/hudson/Launcher.java b/core/src/main/java/hudson/Launcher.java index aeb2c15227..faad258a84 100644 --- a/core/src/main/java/hudson/Launcher.java +++ b/core/src/main/java/hudson/Launcher.java @@ -816,7 +816,7 @@ public abstract class Launcher { * Kill the process when the channel is severed. */ @Override - protected synchronized void terminate(IOException e) { + public synchronized void terminate(IOException e) { super.terminate(e); ProcessTree pt = ProcessTree.get(); try { diff --git a/core/src/main/java/hudson/slaves/Channels.java b/core/src/main/java/hudson/slaves/Channels.java index 09214dc9a4..6f43934203 100644 --- a/core/src/main/java/hudson/slaves/Channels.java +++ b/core/src/main/java/hudson/slaves/Channels.java @@ -73,7 +73,7 @@ public class Channels { * Kill the process when the channel is severed. */ @Override - protected synchronized void terminate(IOException e) { + public synchronized void terminate(IOException e) { super.terminate(e); try { proc.kill(); @@ -109,7 +109,7 @@ public class Channels { * Kill the process when the channel is severed. */ @Override - protected synchronized void terminate(IOException e) { + public synchronized void terminate(IOException e) { super.terminate(e); proc.destroy(); // the stderr copier should exit by itself diff --git a/pom.xml b/pom.xml index 4192f84731..e431e6a24b 100644 --- a/pom.xml +++ b/pom.xml @@ -173,7 +173,7 @@ THE SOFTWARE. org.jenkins-ci.main remoting - 2.40-SNAPSHOT + 2.46-SNAPSHOT From 387804b7ff0994c9005c7387f0d2d8c37462f67f Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Thu, 18 Sep 2014 10:24:21 -0700 Subject: [PATCH 03/54] [SECURITY-144] Provide opportunity to install CallableDecorators before channels are created, to ensure every callable sent gets caught --- .../java/hudson/slaves/ComputerListener.java | 25 ++++++++++++++----- .../java/hudson/slaves/SlaveComputer.java | 12 +++++++-- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/hudson/slaves/ComputerListener.java b/core/src/main/java/hudson/slaves/ComputerListener.java index fc8b04a0b5..173fd0c20f 100644 --- a/core/src/main/java/hudson/slaves/ComputerListener.java +++ b/core/src/main/java/hudson/slaves/ComputerListener.java @@ -23,16 +23,18 @@ */ package hudson.slaves; -import hudson.model.Computer; -import jenkins.model.Jenkins; -import hudson.model.TaskListener; -import hudson.model.Node; -import hudson.ExtensionPoint; +import hudson.AbortException; import hudson.Extension; import hudson.ExtensionList; +import hudson.ExtensionPoint; import hudson.FilePath; +import hudson.model.Computer; +import hudson.model.Node; +import hudson.model.TaskListener; +import hudson.remoting.CallableDecorator; import hudson.remoting.Channel; -import hudson.AbortException; +import hudson.remoting.ChannelBuilder; +import jenkins.model.Jenkins; import java.io.IOException; @@ -43,6 +45,17 @@ import java.io.IOException; * @since 1.246 */ public abstract class ComputerListener implements ExtensionPoint { + + /** + * Called before {@link Channel} is constructed to provide opportunities to affect the way the channel is run. + * + *

+ * Among other things, this is useful to add {@link CallableDecorator}s. + * + * @since 1.THU + */ + public void onChannelBuilding(ChannelBuilder builder, SlaveComputer sc) {} + /** * Called before a {@link ComputerLauncher} is asked to launch a connection with {@link Computer}. * diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java index 05e203784e..70f78785a7 100644 --- a/core/src/main/java/hudson/slaves/SlaveComputer.java +++ b/core/src/main/java/hudson/slaves/SlaveComputer.java @@ -24,6 +24,7 @@ package hudson.slaves; import hudson.model.*; +import hudson.remoting.ChannelBuilder; import hudson.util.IOException2; import hudson.util.IOUtils; import hudson.util.io.ReopenableRotatingFileOutputStream; @@ -72,7 +73,6 @@ import javax.crypto.spec.SecretKeySpec; import javax.servlet.RequestDispatcher; import jenkins.model.Jenkins; import jenkins.slaves.JnlpSlaveAgentProtocol; -import org.acegisecurity.Authentication; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.QueryParameter; @@ -343,7 +343,15 @@ public class SlaveComputer extends Computer { * 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 { - Channel channel = new Channel(nodeName,threadPoolForRemoting, Channel.Mode.NEGOTIATE, in,out, launchLog); + ChannelBuilder cb = new ChannelBuilder(nodeName,threadPoolForRemoting) + .withMode(Channel.Mode.NEGOTIATE) + .withHeaderStream(launchLog); + + for (ComputerListener cl : ComputerListener.all()) { + cl.onChannelBuilding(cb,this); + } + + Channel channel = cb.build(in,out); setChannel(channel,launchLog,listener); } From 2c5666c600d2f589c6cc50c9a20cd5eb1d6481ef Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Thu, 18 Sep 2014 10:37:05 -0700 Subject: [PATCH 04/54] [SECURITY-144] Introduced annotation to mark Callables whether they can be safely invoked from slaves --- .../security/CallableDirectionChecker.java | 69 +++++++++++++++++++ .../java/jenkins/security/MasterToSlave.java | 25 +++++++ .../java/jenkins/security/SlaveToMaster.java | 25 +++++++ 3 files changed, 119 insertions(+) create mode 100644 core/src/main/java/jenkins/security/CallableDirectionChecker.java create mode 100644 core/src/main/java/jenkins/security/MasterToSlave.java create mode 100644 core/src/main/java/jenkins/security/SlaveToMaster.java diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java new file mode 100644 index 0000000000..8fbd68da96 --- /dev/null +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -0,0 +1,69 @@ +package jenkins.security; + +import hudson.Extension; +import hudson.model.Computer; +import hudson.model.TaskListener; +import hudson.remoting.Callable; +import hudson.remoting.CallableDecorator; +import hudson.remoting.ChannelBuilder; +import hudson.slaves.ComputerListener; +import hudson.slaves.SlaveComputer; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Rejects {@link MasterToSlave} callables. + * + * @author Kohsuke Kawaguchi + */ +public class CallableDirectionChecker extends CallableDecorator { + private final SlaveComputer computer; + + public CallableDirectionChecker(SlaveComputer computer) { + this.computer = computer; + } + + @Override + public Callable userRequest(Callable op, Callable stem) { + Class c = op.getClass(); + + boolean m2s = c.isAnnotationPresent(MasterToSlave.class); + boolean s2m = c.isAnnotationPresent(SlaveToMaster.class); + + if (!m2s && !s2m) { + // no annotation provided, so we don't know. + // to err on the correctness we'd let it pass with reporting, which + // provides auditing trail. + if (LOGGER.isLoggable(Level.FINE)) + LOGGER.fine("Unchecked callable from "+computer.getName()+": "+c); + + return stem; + } + + if (s2m) + return stem; // known to be safe + + // this means m2s && !s2m, meaning it's for master->slave + throw new SecurityException(String.format("Invocation of %s is prohibited", c)); + } + + /** + * Installs {@link CallableDirectionChecker} to every channel. + */ + @Extension + public static class ComputerListenerImpl extends ComputerListener { + @Override + public void onChannelBuilding(ChannelBuilder builder, SlaveComputer sc) { + builder.with(new CallableDirectionChecker(sc)); + } + + @Override + public void preLaunch(Computer c, TaskListener taskListener) throws IOException, InterruptedException { + super.preLaunch(c, taskListener); + } + } + + private static final Logger LOGGER = Logger.getLogger(CallableDirectionChecker.class.getName()); +} diff --git a/core/src/main/java/jenkins/security/MasterToSlave.java b/core/src/main/java/jenkins/security/MasterToSlave.java new file mode 100644 index 0000000000..832af307a3 --- /dev/null +++ b/core/src/main/java/jenkins/security/MasterToSlave.java @@ -0,0 +1,25 @@ +package jenkins.security; + +import hudson.remoting.Callable; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotates {@link Callable} that this callable is meant to be run on the slave. + * + * @author Kohsuke Kawaguchi + * @see SlaveToMaster + * @since 1.THU + */ +@Retention(RUNTIME) +@Target(TYPE) +@Inherited +@Documented +public @interface MasterToSlave { +} diff --git a/core/src/main/java/jenkins/security/SlaveToMaster.java b/core/src/main/java/jenkins/security/SlaveToMaster.java new file mode 100644 index 0000000000..9422ad433f --- /dev/null +++ b/core/src/main/java/jenkins/security/SlaveToMaster.java @@ -0,0 +1,25 @@ +package jenkins.security; + +import hudson.remoting.Callable; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +/** + * Annotates {@link Callable} that are meant for the slave to execute on the master. + * + * @author Kohsuke Kawaguchi + * @see MasterToSlave + * @since 1.THU + */ +@Retention(RUNTIME) +@Target(TYPE) +@Inherited +@Documented +public @interface SlaveToMaster { +} From 79ab57ec015dee55adf97759b96e49ec2d9048e3 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Thu, 18 Sep 2014 10:38:36 -0700 Subject: [PATCH 05/54] [FIXED SECURITY-146] Hide system information and diagnostics of the master from slaves --- core/src/main/java/hudson/util/RemotingDiagnostics.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/util/RemotingDiagnostics.java b/core/src/main/java/hudson/util/RemotingDiagnostics.java index 8f014bbcf8..9a95218df7 100644 --- a/core/src/main/java/hudson/util/RemotingDiagnostics.java +++ b/core/src/main/java/hudson/util/RemotingDiagnostics.java @@ -34,6 +34,7 @@ import hudson.remoting.DelegatingCallable; import hudson.remoting.Future; import hudson.remoting.VirtualChannel; import hudson.security.AccessControlled; +import jenkins.security.MasterToSlave; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.ImportCustomizer; import org.kohsuke.stapler.StaplerRequest; @@ -70,6 +71,7 @@ public final class RemotingDiagnostics { return channel.call(new GetSystemProperties()); } + @MasterToSlave private static final class GetSystemProperties implements Callable,RuntimeException> { public Map call() { return new TreeMap(System.getProperties()); @@ -83,12 +85,13 @@ public final class RemotingDiagnostics { return channel.call(new GetThreadDump()); } - public static Future> getThreadDumpAsync(VirtualChannel channel) throws IOException, InterruptedException { + public static Future> getThreakdDumpAsync(VirtualChannel channel) throws IOException, InterruptedException { if(channel==null) return new AsyncFutureImpl>(Collections.singletonMap("N/A","offline")); return channel.callAsync(new GetThreadDump()); } + @MasterToSlave private static final class GetThreadDump implements Callable,RuntimeException> { public Map call() { Map r = new LinkedHashMap(); @@ -119,6 +122,7 @@ public final class RemotingDiagnostics { return channel.call(new Script(script)); } + @MasterToSlave private static final class Script implements DelegatingCallable { private final String script; private transient ClassLoader cl; @@ -184,6 +188,7 @@ public final class RemotingDiagnostics { * Heap dump, exposable to URL via Stapler. * */ + @MasterToSlave public static class HeapDump { private final AccessControlled owner; private final VirtualChannel channel; From 12acbefca63c6b7651b7c38407a31df1145eec15 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Thu, 18 Sep 2014 10:41:39 -0700 Subject: [PATCH 06/54] [FIXED SECURITY-147] This callable allows an arbitrary file to be read from the master --- core/src/main/java/hudson/cli/util/ScriptLoader.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/hudson/cli/util/ScriptLoader.java b/core/src/main/java/hudson/cli/util/ScriptLoader.java index 08df6f6d68..badf963f1f 100644 --- a/core/src/main/java/hudson/cli/util/ScriptLoader.java +++ b/core/src/main/java/hudson/cli/util/ScriptLoader.java @@ -9,14 +9,16 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import jenkins.security.UnsafeBySlave; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; /** - * - * @author vjuranek + * Reads a file (either a path or URL) over a channel. * + * @author vjuranek */ +@UnsafeBySlave public class ScriptLoader implements Callable { private final String script; @@ -43,5 +45,4 @@ public class ScriptLoader implements Callable { s.close(); } } - } From 91d3febd6dd71ef46b72ca7872f46062683fdf51 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Thu, 18 Sep 2014 15:01:06 -0700 Subject: [PATCH 07/54] [SECURITY-144] Make annotation of anonymous Callables easier We often create anonymous Callable types, yet anonymous class cannot be annotated. These convenient base classes make that transition easy --- .../jenkins/security/MasterToSlaveCallable.java | 14 ++++++++++++++ .../jenkins/security/SlaveToMasterCallable.java | 14 ++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 core/src/main/java/jenkins/security/MasterToSlaveCallable.java create mode 100644 core/src/main/java/jenkins/security/SlaveToMasterCallable.java diff --git a/core/src/main/java/jenkins/security/MasterToSlaveCallable.java b/core/src/main/java/jenkins/security/MasterToSlaveCallable.java new file mode 100644 index 0000000000..1834a08560 --- /dev/null +++ b/core/src/main/java/jenkins/security/MasterToSlaveCallable.java @@ -0,0 +1,14 @@ +package jenkins.security; + +import hudson.remoting.Callable; + +/** + * Convenient {@link Callable} with {@link MasterToSlave} to create anonymous Callable class. + * + * @author Kohsuke Kawaguchi + * @since 1.THU + */ +@MasterToSlave +public abstract class MasterToSlaveCallable implements Callable { + private static final long serialVersionUID = 1L; +} diff --git a/core/src/main/java/jenkins/security/SlaveToMasterCallable.java b/core/src/main/java/jenkins/security/SlaveToMasterCallable.java new file mode 100644 index 0000000000..0a05dc22d8 --- /dev/null +++ b/core/src/main/java/jenkins/security/SlaveToMasterCallable.java @@ -0,0 +1,14 @@ +package jenkins.security; + +import hudson.remoting.Callable; + +/** + * Convenient {@link Callable} with {@link SlaveToMaster} to create anonymous Callable class. + * + * @author Kohsuke Kawaguchi + * @since 1.THU + */ +@SlaveToMaster +public abstract class SlaveToMasterCallable implements Callable { + private static final long serialVersionUID = 1L; +} From 101b4e7fd5c3bbb2651ee2492519a92701d0bab4 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Thu, 18 Sep 2014 14:57:52 -0700 Subject: [PATCH 08/54] [SECURITY-144] Classify all the core callables one way or the other --- core/src/main/java/hudson/EnvVars.java | 2 + core/src/main/java/hudson/FilePath.java | 6 ++- core/src/main/java/hudson/Launcher.java | 4 ++ core/src/main/java/hudson/cli/CLICommand.java | 5 +++ .../hudson/cli/ClientAuthenticationCache.java | 3 +- .../java/hudson/cli/CommandDuringBuild.java | 2 + .../java/hudson/cli/InstallToolCommand.java | 2 + .../java/hudson/cli/util/ScriptLoader.java | 9 ++-- .../main/java/hudson/logging/LogRecorder.java | 2 + core/src/main/java/hudson/model/Computer.java | 4 ++ core/src/main/java/hudson/model/Slave.java | 4 ++ .../node_monitors/ArchitectureMonitor.java | 2 + .../node_monitors/ResponseTimeMonitor.java | 5 +++ .../node_monitors/SwapSpaceMonitor.java | 2 + .../java/hudson/os/solaris/ZFSInstaller.java | 3 +- .../AbstractPasswordBasedSecurityRealm.java | 2 + .../java/hudson/slaves/ChannelPinger.java | 2 + .../slaves/ConnectionActivityMonitor.java | 3 ++ .../java/hudson/slaves/SlaveComputer.java | 10 +++++ core/src/main/java/hudson/tasks/Maven.java | 5 ++- core/src/main/java/hudson/tasks/Shell.java | 3 ++ .../main/java/hudson/tools/JDKInstaller.java | 3 ++ .../main/java/hudson/util/ProcessTree.java | 4 +- .../java/hudson/util/RemotingDiagnostics.java | 41 ++++++++++--------- .../hudson/util/io/ParserConfigurator.java | 4 +- core/src/main/java/jenkins/model/Jenkins.java | 3 +- .../jenkins/slaves/StandardOutputSwapper.java | 2 + .../hudson/maven/AbstractMavenBuilder.java | 2 + .../maven/AbstractMavenProcessFactory.java | 8 +++- .../hudson/maven/Maven31ProcessFactory.java | 2 + .../main/java/hudson/maven/Maven3Builder.java | 2 +- .../hudson/maven/Maven3ProcessFactory.java | 2 + .../java/hudson/maven/MavenBuildProxy.java | 2 + .../main/java/hudson/maven/MavenBuilder.java | 23 +++++------ .../hudson/maven/MavenProcessFactory.java | 3 ++ .../hudson/maven/MavenVersionCallable.java | 3 +- .../main/java/hudson/maven/ProcessCache.java | 4 ++ .../java/hudson/maven/RedeployPublisher.java | 2 + .../hudson/maven/SplittableBuildListener.java | 2 + .../java/hudson/slaves/JNLPLauncherTest.java | 3 ++ 40 files changed, 145 insertions(+), 50 deletions(-) diff --git a/core/src/main/java/hudson/EnvVars.java b/core/src/main/java/hudson/EnvVars.java index 0ad75f775d..b0a9962259 100644 --- a/core/src/main/java/hudson/EnvVars.java +++ b/core/src/main/java/hudson/EnvVars.java @@ -26,6 +26,7 @@ package hudson; import hudson.remoting.Callable; import hudson.remoting.VirtualChannel; import hudson.util.CaseInsensitiveComparator; +import jenkins.security.MasterToSlave; import java.io.File; import java.io.IOException; @@ -212,6 +213,7 @@ public class EnvVars extends TreeMap { return channel.call(new GetEnvVars()); } + @MasterToSlave private static final class GetEnvVars implements Callable { public EnvVars call() { return new EnvVars(EnvVars.masterEnvVars); diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 322607d9b5..f6e01374ac 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -53,6 +53,9 @@ import static hudson.FilePath.TarCompression.GZIP; import hudson.org.apache.tools.tar.TarInputStream; import hudson.util.io.Archiver; import hudson.util.io.ArchiverFactory; +import jenkins.security.MasterToSlave; +import jenkins.security.MasterToSlaveCallable; +import jenkins.security.SlaveToMaster; import jenkins.util.VirtualFile; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Project; @@ -2091,6 +2094,7 @@ public final class FilePath implements Serializable { return new RemoteLauncher(listener,channel,channel.call(new IsUnix())); } + @MasterToSlave @SlaveToMaster private static final class IsUnix implements Callable { public Boolean call() throws IOException { return File.pathSeparatorChar==':'; @@ -2489,7 +2493,7 @@ public final class FilePath implements Serializable { * (User's home directory in the Unix sense) of the given channel. */ public static FilePath getHomeDirectory(VirtualChannel ch) throws InterruptedException, IOException { - return ch.call(new Callable() { + return ch.call(new MasterToSlaveCallable() { public FilePath call() throws IOException { return new FilePath(new File(System.getProperty("user.home"))); } diff --git a/core/src/main/java/hudson/Launcher.java b/core/src/main/java/hudson/Launcher.java index faad258a84..6f1b4b2205 100644 --- a/core/src/main/java/hudson/Launcher.java +++ b/core/src/main/java/hudson/Launcher.java @@ -38,6 +38,7 @@ import hudson.remoting.VirtualChannel; import hudson.util.StreamCopyThread; import hudson.util.ArgumentListBuilder; import hudson.util.ProcessTree; +import jenkins.security.MasterToSlave; import org.apache.commons.io.input.NullInputStream; import java.io.BufferedOutputStream; @@ -887,6 +888,7 @@ public abstract class Launcher { getChannel().call(new KillTask(modelEnvVars)); } + @MasterToSlave private static final class KillTask implements Callable { private final Map modelEnvVars; @@ -962,6 +964,7 @@ public abstract class Launcher { IOTriplet getIOtriplet(); } + @MasterToSlave private static class RemoteLaunchCallable implements Callable { private final List cmd; private final boolean[] masks; @@ -1032,6 +1035,7 @@ public abstract class Launcher { private static final long serialVersionUID = 1L; } + @MasterToSlave private static class RemoteChannelLaunchCallable implements Callable { private final String[] cmd; private final Pipe out; diff --git a/core/src/main/java/hudson/cli/CLICommand.java b/core/src/main/java/hudson/cli/CLICommand.java index dc8b9fcba3..0efb64f3f9 100644 --- a/core/src/main/java/hudson/cli/CLICommand.java +++ b/core/src/main/java/hudson/cli/CLICommand.java @@ -36,6 +36,8 @@ import hudson.remoting.Channel; import hudson.remoting.ChannelProperty; import hudson.security.CliAuthenticator; import hudson.security.SecurityRealm; +import jenkins.security.MasterToSlave; +import jenkins.security.SlaveToMaster; import org.acegisecurity.Authentication; import org.acegisecurity.BadCredentialsException; import org.acegisecurity.context.SecurityContext; @@ -359,6 +361,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable { return checkChannel().call(new GetSystemProperty(name)); } + @MasterToSlave private static final class GetSystemProperty implements Callable { private final String name; @@ -388,6 +391,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable { } } + @MasterToSlave @SlaveToMaster private static final class GetCharset implements Callable { public String call() throws IOException { return Charset.defaultCharset().name(); @@ -403,6 +407,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable { return checkChannel().call(new GetEnvironmentVariable(name)); } + @MasterToSlave private static final class GetEnvironmentVariable implements Callable { private final String name; diff --git a/core/src/main/java/hudson/cli/ClientAuthenticationCache.java b/core/src/main/java/hudson/cli/ClientAuthenticationCache.java index cd727e7ba4..f04143a0ba 100644 --- a/core/src/main/java/hudson/cli/ClientAuthenticationCache.java +++ b/core/src/main/java/hudson/cli/ClientAuthenticationCache.java @@ -6,6 +6,7 @@ import jenkins.model.Jenkins.MasterComputer; import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.util.Secret; +import jenkins.security.MasterToSlaveCallable; import org.acegisecurity.Authentication; import org.acegisecurity.AuthenticationException; import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; @@ -40,7 +41,7 @@ public class ClientAuthenticationCache implements Serializable { private final Properties props = new Properties(); public ClientAuthenticationCache(Channel channel) throws IOException, InterruptedException { - store = (channel==null ? MasterComputer.localChannel : channel).call(new Callable() { + store = (channel==null ? MasterComputer.localChannel : channel).call(new MasterToSlaveCallable() { public FilePath call() throws IOException { File home = new File(System.getProperty("user.home")); return new FilePath(new File(home, ".hudson/cli-credentials")); diff --git a/core/src/main/java/hudson/cli/CommandDuringBuild.java b/core/src/main/java/hudson/cli/CommandDuringBuild.java index 6f9230b3ee..759563744a 100644 --- a/core/src/main/java/hudson/cli/CommandDuringBuild.java +++ b/core/src/main/java/hudson/cli/CommandDuringBuild.java @@ -28,6 +28,7 @@ import jenkins.model.Jenkins; import hudson.model.Job; import hudson.model.Run; import hudson.remoting.Callable; +import jenkins.security.MasterToSlave; import org.kohsuke.args4j.CmdLineException; import java.io.IOException; @@ -81,6 +82,7 @@ public abstract class CommandDuringBuild extends CLICommand { /** * Gets the environment variables that points to the build being executed. */ + @MasterToSlave private static final class GetCharacteristicEnvironmentVariables implements Callable { public String[] call() throws IOException { return new String[] { diff --git a/core/src/main/java/hudson/cli/InstallToolCommand.java b/core/src/main/java/hudson/cli/InstallToolCommand.java index fb30efd0c0..e39d90fba4 100644 --- a/core/src/main/java/hudson/cli/InstallToolCommand.java +++ b/core/src/main/java/hudson/cli/InstallToolCommand.java @@ -44,6 +44,7 @@ import java.util.List; import java.util.ArrayList; import java.io.IOException; +import jenkins.security.MasterToSlave; import org.kohsuke.args4j.Argument; /** @@ -128,6 +129,7 @@ public class InstallToolCommand extends CLICommand { return 0; } + @MasterToSlave private static final class BuildIDs implements Callable { String job,number,id; diff --git a/core/src/main/java/hudson/cli/util/ScriptLoader.java b/core/src/main/java/hudson/cli/util/ScriptLoader.java index badf963f1f..5b590ac313 100644 --- a/core/src/main/java/hudson/cli/util/ScriptLoader.java +++ b/core/src/main/java/hudson/cli/util/ScriptLoader.java @@ -2,6 +2,9 @@ package hudson.cli.util; import hudson.AbortException; import hudson.remoting.Callable; +import jenkins.security.MasterToSlave; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; import java.io.File; import java.io.IOException; @@ -9,16 +12,12 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; -import jenkins.security.UnsafeBySlave; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; - /** * Reads a file (either a path or URL) over a channel. * * @author vjuranek */ -@UnsafeBySlave +@MasterToSlave public class ScriptLoader implements Callable { private final String script; diff --git a/core/src/main/java/hudson/logging/LogRecorder.java b/core/src/main/java/hudson/logging/LogRecorder.java index f780252af4..9cc456f9f4 100644 --- a/core/src/main/java/hudson/logging/LogRecorder.java +++ b/core/src/main/java/hudson/logging/LogRecorder.java @@ -42,6 +42,7 @@ import hudson.slaves.ComputerListener; import hudson.util.CopyOnWriteList; import hudson.util.RingBufferLogHandler; import hudson.util.XStream2; +import jenkins.security.MasterToSlave; import net.sf.json.JSONObject; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; @@ -160,6 +161,7 @@ public class LogRecorder extends AbstractModelObject implements Saveable { } + @MasterToSlave private static final class SetLevel implements Callable { /** known loggers (kept per slave), to avoid GC */ @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") private static final Set loggers = new HashSet(); diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index b4a052349e..42c2c76b89 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -58,6 +58,7 @@ import hudson.util.RemotingDiagnostics.HeapDump; import hudson.util.RunList; import hudson.util.Futures; import jenkins.model.Jenkins; +import jenkins.security.MasterToSlave; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.stapler.StaplerRequest; @@ -1024,6 +1025,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces oneOffExecutors.remove(e); } + @MasterToSlave private static class ListPossibleNames implements Callable,IOException> { public List call() throws IOException { List names = new ArrayList(); @@ -1054,6 +1056,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces private static final long serialVersionUID = 1L; } + @MasterToSlave private static class GetFallbackName implements Callable { public String call() throws IOException { return System.getProperty("host.name"); @@ -1143,6 +1146,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces w.close(); } + @MasterToSlave private static final class DumpExportTableTask implements Callable { public String call() throws IOException { StringWriter sw = new StringWriter(); diff --git a/core/src/main/java/hudson/model/Slave.java b/core/src/main/java/hudson/model/Slave.java index 46b4849596..35bdee8631 100644 --- a/core/src/main/java/hudson/model/Slave.java +++ b/core/src/main/java/hudson/model/Slave.java @@ -58,6 +58,7 @@ import javax.servlet.ServletException; import hudson.util.TimeUnit2; import jenkins.model.Jenkins; +import jenkins.security.MasterToSlave; import jenkins.slaves.WorkspaceLocator; import org.apache.commons.io.IOUtils; @@ -440,6 +441,7 @@ public abstract class Slave extends Node implements Serializable { *

  • When it's read on this side as a return value, it morphs itself into {@link ClockDifference}. * */ + @MasterToSlave private static final class GetClockDifference1 implements Callable { public ClockDifference call() { // this method must be being invoked locally, which means the clock is in sync @@ -453,6 +455,7 @@ public abstract class Slave extends Node implements Serializable { private static final long serialVersionUID = 1L; } + @MasterToSlave private static final class GetClockDifference2 implements Callable { /** * Capture the time on the master when this object is sent to remote, which is when @@ -467,6 +470,7 @@ public abstract class Slave extends Node implements Serializable { private static final long serialVersionUID = 1L; } + @MasterToSlave private static final class GetClockDifference3 implements Serializable { private final long remoteTime = System.currentTimeMillis(); private final long startTime; diff --git a/core/src/main/java/hudson/node_monitors/ArchitectureMonitor.java b/core/src/main/java/hudson/node_monitors/ArchitectureMonitor.java index 9f127a7f72..05c59a91dd 100644 --- a/core/src/main/java/hudson/node_monitors/ArchitectureMonitor.java +++ b/core/src/main/java/hudson/node_monitors/ArchitectureMonitor.java @@ -26,6 +26,7 @@ package hudson.node_monitors; import hudson.model.Computer; import hudson.remoting.Callable; import hudson.Extension; +import jenkins.security.MasterToSlave; import net.sf.json.JSONObject; import org.kohsuke.stapler.StaplerRequest; @@ -56,6 +57,7 @@ public class ArchitectureMonitor extends NodeMonitor { /** * Obtains the string that represents the architecture. */ + @MasterToSlave private static class GetArchTask implements Callable { public String call() { String os = System.getProperty("os.name"); diff --git a/core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java b/core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java index 6248b84d86..491b991e7a 100644 --- a/core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java +++ b/core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java @@ -32,6 +32,8 @@ import hudson.remoting.Future; import hudson.util.ClockDifference; import hudson.util.TimeUnit2; import hudson.util.IOException2; +import jenkins.security.MasterToSlave; +import jenkins.security.SlaveToMaster; import net.sf.json.JSONObject; import org.kohsuke.stapler.StaplerRequest; @@ -92,6 +94,7 @@ public class ResponseTimeMonitor extends NodeMonitor { } }; + @MasterToSlave private static final class Step1 implements Callable { private Data cur; @@ -111,6 +114,7 @@ public class ResponseTimeMonitor extends NodeMonitor { private static final long serialVersionUID = 1L; } + @MasterToSlave private static final class Step2 implements Callable { private final Data cur; private final long start = System.currentTimeMillis(); @@ -127,6 +131,7 @@ public class ResponseTimeMonitor extends NodeMonitor { private static final long serialVersionUID = 1L; } + @SlaveToMaster private static final class Step3 implements Serializable { private final Data cur; private final long start; diff --git a/core/src/main/java/hudson/node_monitors/SwapSpaceMonitor.java b/core/src/main/java/hudson/node_monitors/SwapSpaceMonitor.java index e09efe3c17..a72b94e84d 100644 --- a/core/src/main/java/hudson/node_monitors/SwapSpaceMonitor.java +++ b/core/src/main/java/hudson/node_monitors/SwapSpaceMonitor.java @@ -28,6 +28,7 @@ import hudson.Extension; import hudson.model.Computer; import jenkins.model.Jenkins; import hudson.remoting.Callable; +import jenkins.security.MasterToSlave; import net.sf.json.JSONObject; import org.jvnet.hudson.MemoryMonitor; import org.jvnet.hudson.MemoryUsage; @@ -97,6 +98,7 @@ public class SwapSpaceMonitor extends NodeMonitor { /** * Obtains the string that represents the architecture. */ + @MasterToSlave private static class MonitorTask implements Callable { public MemoryUsage call() throws IOException { MemoryMonitor mm; diff --git a/core/src/main/java/hudson/os/solaris/ZFSInstaller.java b/core/src/main/java/hudson/os/solaris/ZFSInstaller.java index ec0d51ccb8..17a3bb48ac 100644 --- a/core/src/main/java/hudson/os/solaris/ZFSInstaller.java +++ b/core/src/main/java/hudson/os/solaris/ZFSInstaller.java @@ -38,6 +38,7 @@ import hudson.util.HudsonIsRestarting; import hudson.util.StreamTaskListener; import static hudson.util.jna.GNUCLibrary.*; +import jenkins.security.MasterToSlaveCallable; import org.apache.commons.io.output.ByteArrayOutputStream; import org.jvnet.libpam.impl.CLibrary.passwd; import org.jvnet.solaris.libzfs.ACLBuilder; @@ -167,7 +168,7 @@ public class ZFSInstaller extends AdministrativeMonitor implements Serializable // this is the actual creation of the file system. // return true indicating a success - return SU.execute(listener, rootUsername, rootPassword, new Callable() { + return SU.execute(listener, rootUsername, rootPassword, new MasterToSlaveCallable() { private static final long serialVersionUID = 7731167233498214301L; public String call() throws IOException { diff --git a/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java b/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java index de3b806f60..84e9b163d6 100644 --- a/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java +++ b/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java @@ -6,6 +6,7 @@ import hudson.cli.CLICommand; import jenkins.model.Jenkins; import hudson.remoting.Callable; import hudson.util.spring.BeanBuilder; +import jenkins.security.MasterToSlave; import org.acegisecurity.Authentication; import org.acegisecurity.AuthenticationException; import org.acegisecurity.AuthenticationManager; @@ -140,6 +141,7 @@ public abstract class AbstractPasswordBasedSecurityRealm extends SecurityRealm i /** * Asks for the password. */ + @MasterToSlave private static class InteractivelyAskForPassword implements Callable { @IgnoreJRERequirement public String call() throws IOException { diff --git a/core/src/main/java/hudson/slaves/ChannelPinger.java b/core/src/main/java/hudson/slaves/ChannelPinger.java index 4141af49b8..f7de0f0349 100644 --- a/core/src/main/java/hudson/slaves/ChannelPinger.java +++ b/core/src/main/java/hudson/slaves/ChannelPinger.java @@ -31,6 +31,7 @@ import hudson.model.TaskListener; import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.remoting.PingThread; +import jenkins.security.MasterToSlave; import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; @@ -85,6 +86,7 @@ public class ChannelPinger extends ComputerListener { setUpPingForChannel(channel, pingInterval); } + @MasterToSlave private static class SetUpRemotePing implements Callable { private static final long serialVersionUID = -2702219700841759872L; private int pingInterval; diff --git a/core/src/main/java/hudson/slaves/ConnectionActivityMonitor.java b/core/src/main/java/hudson/slaves/ConnectionActivityMonitor.java index 03890303d1..997a47525f 100644 --- a/core/src/main/java/hudson/slaves/ConnectionActivityMonitor.java +++ b/core/src/main/java/hudson/slaves/ConnectionActivityMonitor.java @@ -32,6 +32,8 @@ import hudson.remoting.VirtualChannel; import hudson.remoting.Channel; import hudson.remoting.Callable; import hudson.Extension; +import jenkins.security.MasterToSlave; +import jenkins.security.SlaveToMaster; import java.io.IOException; import java.util.logging.Logger; @@ -102,6 +104,7 @@ public class ConnectionActivityMonitor extends AsyncPeriodicWork { public boolean enabled = Boolean.getBoolean(ConnectionActivityMonitor.class.getName()+".enabled"); private static final PingCommand PING_COMMAND = new PingCommand(); + @MasterToSlave @SlaveToMaster private static final class PingCommand implements Callable { public Void call() throws RuntimeException { return null; diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java index 70f78785a7..1b7348128b 100644 --- a/core/src/main/java/hudson/slaves/SlaveComputer.java +++ b/core/src/main/java/hudson/slaves/SlaveComputer.java @@ -72,6 +72,8 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import javax.servlet.RequestDispatcher; import jenkins.model.Jenkins; +import jenkins.security.MasterToSlave; +import jenkins.security.SlaveToMaster; import jenkins.slaves.JnlpSlaveAgentProtocol; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -399,6 +401,7 @@ public class SlaveComputer extends Computer { return channel.call(new LoadingTime(true)); } + @MasterToSlave @SlaveToMaster static class LoadingCount implements Callable { private final boolean resource; LoadingCount(boolean resource) { @@ -410,12 +413,14 @@ public class SlaveComputer extends Computer { } } + @MasterToSlave static class LoadingPrefetchCacheCount implements Callable { @Override public Integer call() { return Channel.current().classLoadingPrefetchCacheCount.get(); } } + @MasterToSlave static class LoadingTime implements Callable { private final boolean resource; LoadingTime(boolean resource) { @@ -711,18 +716,21 @@ public class SlaveComputer extends Computer { private static final Logger logger = Logger.getLogger(SlaveComputer.class.getName()); + @MasterToSlave private static final class SlaveVersion implements Callable { public String call() throws IOException { try { return Launcher.VERSION; } catch (Throwable ex) { return "< 1.335"; } // Older slave.jar won't have VERSION } } + @MasterToSlave @SlaveToMaster private static final class DetectOS implements Callable { public Boolean call() throws IOException { return File.pathSeparatorChar==':'; } } + @MasterToSlave @SlaveToMaster private static final class DetectDefaultCharset implements Callable { public String call() throws IOException { return Charset.defaultCharset().name(); @@ -740,6 +748,7 @@ public class SlaveComputer extends Computer { static final RingBufferLogHandler SLAVE_LOG_HANDLER = new RingBufferLogHandler(); } + @MasterToSlave private static class SlaveInitializer implements Callable { public Void call() { // avoid double installation of the handler. JNLP slaves can reconnect to the master multiple times @@ -787,6 +796,7 @@ public class SlaveComputer extends Computer { return null; } + @MasterToSlave private static class SlaveLogFetcher implements Callable,RuntimeException> { public List call() { return new ArrayList(SLAVE_LOG_HANDLER.getView()); diff --git a/core/src/main/java/hudson/tasks/Maven.java b/core/src/main/java/hudson/tasks/Maven.java index 609947de10..aed029341e 100644 --- a/core/src/main/java/hudson/tasks/Maven.java +++ b/core/src/main/java/hudson/tasks/Maven.java @@ -59,6 +59,7 @@ import hudson.util.VariableResolver.ByMap; import hudson.util.VariableResolver.Union; import hudson.util.FormValidation; import hudson.util.XStream2; +import jenkins.security.MasterToSlaveCallable; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; @@ -507,7 +508,7 @@ public class Maven extends Builder { public boolean meetsMavenReqVersion(Launcher launcher, int mavenReqVersion) throws IOException, InterruptedException { // FIXME using similar stuff as in the maven plugin could be better // olamy : but will add a dependency on maven in core -> so not so good - String mavenVersion = launcher.getChannel().call(new Callable() { + String mavenVersion = launcher.getChannel().call(new MasterToSlaveCallable() { private static final long serialVersionUID = -4143159957567745621L; public String call() throws IOException { @@ -563,7 +564,7 @@ public class Maven extends Builder { * Gets the executable path of this maven on the given target system. */ public String getExecutable(Launcher launcher) throws IOException, InterruptedException { - return launcher.getChannel().call(new Callable() { + return launcher.getChannel().call(new MasterToSlaveCallable() { private static final long serialVersionUID = 2373163112639943768L; public String call() throws IOException { diff --git a/core/src/main/java/hudson/tasks/Shell.java b/core/src/main/java/hudson/tasks/Shell.java index 346a124e4a..64c0192f6f 100644 --- a/core/src/main/java/hudson/tasks/Shell.java +++ b/core/src/main/java/hudson/tasks/Shell.java @@ -32,6 +32,8 @@ import hudson.remoting.Callable; import hudson.remoting.VirtualChannel; import hudson.util.FormValidation; import java.io.IOException; + +import jenkins.security.MasterToSlave; import net.sf.json.JSONObject; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; @@ -193,6 +195,7 @@ public class Shell extends CommandInterpreter { return FormValidation.validateExecutable(value); } + @MasterToSlave private static final class Shellinterpreter implements Callable { private static final long serialVersionUID = 1L; diff --git a/core/src/main/java/hudson/tools/JDKInstaller.java b/core/src/main/java/hudson/tools/JDKInstaller.java index ac5e311dbb..2b6fc24327 100644 --- a/core/src/main/java/hudson/tools/JDKInstaller.java +++ b/core/src/main/java/hudson/tools/JDKInstaller.java @@ -40,6 +40,7 @@ import hudson.util.FormValidation; import hudson.util.HttpResponses; import hudson.util.Secret; import jenkins.model.Jenkins; +import jenkins.security.MasterToSlave; import net.sf.json.JSONObject; import org.apache.commons.httpclient.Cookie; import org.apache.commons.httpclient.HttpClient; @@ -501,6 +502,7 @@ public class JDKInstaller extends ToolInstaller { throw new DetectionFailedException("Unknown CPU name: "+arch); } + @MasterToSlave static class GetCurrentPlatform implements Callable { private static final long serialVersionUID = 1L; public Platform call() throws DetectionFailedException { @@ -563,6 +565,7 @@ public class JDKInstaller extends ToolInstaller { throw new DetectionFailedException("Unknown CPU architecture: "+arch); } + @MasterToSlave static class GetCurrentCPU implements Callable { private static final long serialVersionUID = 1L; public CPU call() throws DetectionFailedException { diff --git a/core/src/main/java/hudson/util/ProcessTree.java b/core/src/main/java/hudson/util/ProcessTree.java index 2dd927d6fa..3145b63d70 100644 --- a/core/src/main/java/hudson/util/ProcessTree.java +++ b/core/src/main/java/hudson/util/ProcessTree.java @@ -29,13 +29,13 @@ import com.sun.jna.ptr.IntByReference; import hudson.EnvVars; import hudson.Util; import jenkins.model.Jenkins; -import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.remoting.VirtualChannel; import hudson.slaves.SlaveComputer; import hudson.util.ProcessTree.OSProcess; import hudson.util.ProcessTreeRemoting.IOSProcess; import hudson.util.ProcessTreeRemoting.IProcessTree; +import jenkins.security.SlaveToMasterCallable; import org.apache.commons.io.FileUtils; import org.jvnet.winp.WinProcess; import org.jvnet.winp.WinpException; @@ -158,7 +158,7 @@ public abstract class ProcessTree implements Iterable, IProcessTree, try { VirtualChannel channelToMaster = SlaveComputer.getChannelToMaster(); if (channelToMaster!=null) { - killers = channelToMaster.call(new Callable, IOException>() { + killers = channelToMaster.call(new SlaveToMasterCallable, IOException>() { public List call() throws IOException { return new ArrayList(ProcessKiller.all()); } diff --git a/core/src/main/java/hudson/util/RemotingDiagnostics.java b/core/src/main/java/hudson/util/RemotingDiagnostics.java index 9a95218df7..a0edd54abf 100644 --- a/core/src/main/java/hudson/util/RemotingDiagnostics.java +++ b/core/src/main/java/hudson/util/RemotingDiagnostics.java @@ -85,7 +85,7 @@ public final class RemotingDiagnostics { return channel.call(new GetThreadDump()); } - public static Future> getThreakdDumpAsync(VirtualChannel channel) throws IOException, InterruptedException { + public static Future> getThreadDumpAsync(VirtualChannel channel) throws IOException, InterruptedException { if(channel==null) return new AsyncFutureImpl>(Collections.singletonMap("N/A","offline")); return channel.callAsync(new GetThreadDump()); @@ -165,30 +165,12 @@ public final class RemotingDiagnostics { * Obtains the heap dump in an HPROF file. */ public static FilePath getHeapDump(VirtualChannel channel) throws IOException, InterruptedException { - return channel.call(new Callable() { - public FilePath call() throws IOException { - final File hprof = File.createTempFile("hudson-heapdump", "hprof"); - hprof.delete(); - try { - MBeanServer server = ManagementFactory.getPlatformMBeanServer(); - server.invoke(new ObjectName("com.sun.management:type=HotSpotDiagnostic"), "dumpHeap", - new Object[]{hprof.getAbsolutePath(), true}, new String[]{String.class.getName(), boolean.class.getName()}); - - return new FilePath(hprof); - } catch (JMException e) { - throw new IOException2(e); - } - } - - private static final long serialVersionUID = 1L; - }); + return channel.call(new GetHeapDump()); } /** * Heap dump, exposable to URL via Stapler. - * */ - @MasterToSlave public static class HeapDump { private final AccessControlled owner; private final VirtualChannel channel; @@ -222,4 +204,23 @@ public final class RemotingDiagnostics { return RemotingDiagnostics.getHeapDump(channel); } } + + @MasterToSlave + private static class GetHeapDump implements Callable { + public FilePath call() throws IOException { + final File hprof = File.createTempFile("hudson-heapdump", "hprof"); + hprof.delete(); + try { + MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + server.invoke(new ObjectName("com.sun.management:type=HotSpotDiagnostic"), "dumpHeap", + new Object[]{hprof.getAbsolutePath(), true}, new String[]{String.class.getName(), boolean.class.getName()}); + + return new FilePath(hprof); + } catch (JMException e) { + throw new IOException2(e); + } + } + + private static final long serialVersionUID = 1L; + } } diff --git a/core/src/main/java/hudson/util/io/ParserConfigurator.java b/core/src/main/java/hudson/util/io/ParserConfigurator.java index cd4a8642e2..dde6a1d342 100644 --- a/core/src/main/java/hudson/util/io/ParserConfigurator.java +++ b/core/src/main/java/hudson/util/io/ParserConfigurator.java @@ -25,9 +25,9 @@ package hudson.util.io; import hudson.ExtensionList; import hudson.ExtensionPoint; -import hudson.remoting.Callable; import hudson.remoting.Channel; import jenkins.model.Jenkins; +import jenkins.security.SlaveToMasterCallable; import org.dom4j.io.SAXReader; import java.io.IOException; @@ -78,7 +78,7 @@ public abstract class ParserConfigurator implements ExtensionPoint, Serializable if (Jenkins.getInstance()==null) { Channel ch = Channel.current(); if (ch!=null) - all = ch.call(new Callable, IOException>() { + all = ch.call(new SlaveToMasterCallable, IOException>() { private static final long serialVersionUID = -2178106894481500733L; diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 1dc792015a..18ac0e6331 100755 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -200,6 +200,7 @@ import jenkins.InitReactorRunner; import jenkins.model.ProjectNamingStrategy.DefaultProjectNamingStrategy; import jenkins.security.ConfidentialKey; import jenkins.security.ConfidentialStore; +import jenkins.security.MasterToSlaveCallable; import jenkins.slaves.WorkspaceLocator; import jenkins.util.io.FileBoolean; import net.sf.json.JSONObject; @@ -1966,7 +1967,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGro @Override public Callable getClockDifferenceCallable() { - return new Callable() { + return new MasterToSlaveCallable() { public ClockDifference call() throws IOException { return new ClockDifference(0); } diff --git a/core/src/main/java/jenkins/slaves/StandardOutputSwapper.java b/core/src/main/java/jenkins/slaves/StandardOutputSwapper.java index 4abcf3c409..f0bb18593d 100644 --- a/core/src/main/java/jenkins/slaves/StandardOutputSwapper.java +++ b/core/src/main/java/jenkins/slaves/StandardOutputSwapper.java @@ -9,6 +9,7 @@ import hudson.remoting.Channel; import hudson.remoting.StandardOutputStream; import hudson.slaves.ComputerListener; import hudson.util.jna.GNUCLibrary; +import jenkins.security.MasterToSlave; import java.io.File; import java.io.FileDescriptor; @@ -35,6 +36,7 @@ public class StandardOutputSwapper extends ComputerListener { } } + @MasterToSlave private static final class ChannelSwapper implements Callable { public Boolean call() throws Exception { if (File.pathSeparatorChar==';') return false; // Windows diff --git a/maven-plugin/src/main/java/hudson/maven/AbstractMavenBuilder.java b/maven-plugin/src/main/java/hudson/maven/AbstractMavenBuilder.java index 353954645c..9ad0316d8a 100644 --- a/maven-plugin/src/main/java/hudson/maven/AbstractMavenBuilder.java +++ b/maven-plugin/src/main/java/hudson/maven/AbstractMavenBuilder.java @@ -43,12 +43,14 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutionException; import jenkins.model.Jenkins; +import jenkins.security.MasterToSlave; /** * @author Olivier Lamy * @author Christoph Kutzinski * */ +@MasterToSlave public abstract class AbstractMavenBuilder implements DelegatingCallable { private static final long serialVersionUID = -2687215937784908860L; diff --git a/maven-plugin/src/main/java/hudson/maven/AbstractMavenProcessFactory.java b/maven-plugin/src/main/java/hudson/maven/AbstractMavenProcessFactory.java index fb1adefab9..d004f394db 100644 --- a/maven-plugin/src/main/java/hudson/maven/AbstractMavenProcessFactory.java +++ b/maven-plugin/src/main/java/hudson/maven/AbstractMavenProcessFactory.java @@ -43,6 +43,8 @@ import java.nio.charset.UnsupportedCharsetException; import java.util.Arrays; import jenkins.model.Jenkins; +import jenkins.security.MasterToSlave; +import jenkins.security.SlaveToMaster; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Zip; @@ -157,6 +159,7 @@ public abstract class AbstractMavenProcessFactory * Opens a server socket and returns {@link Acceptor} so that * we can accept a connection later on it. */ + @MasterToSlave private static final class SocketHandler implements Callable { public Acceptor call() throws IOException { return new AcceptorImpl(); @@ -199,6 +202,7 @@ public abstract class AbstractMavenProcessFactory } } + @MasterToSlave private static final class GetCharset implements Callable { private static final long serialVersionUID = 3459269768733083577L; @@ -284,6 +288,7 @@ public abstract class AbstractMavenProcessFactory } /** Verifies that the channel is open and functioning, and (if the second time around) sets properties for the original JDK. */ + @MasterToSlave private static final class ConfigureOriginalJDK implements Callable { private static final long serialVersionUID = 1; private final JDK jdk; @@ -302,6 +307,7 @@ public abstract class AbstractMavenProcessFactory } /** Locates JRE this slave agent is running on, or null. */ + @MasterToSlave @SlaveToMaster private static final class FindJavaHome implements Callable { private static final long serialVersionUID = 1; @Override public JDK call() throws Error { @@ -487,7 +493,7 @@ public abstract class AbstractMavenProcessFactory return jdk; } - + @MasterToSlave @SlaveToMaster protected static final class GetRemotingJar implements Callable { private static final long serialVersionUID = 6022357183425911351L; diff --git a/maven-plugin/src/main/java/hudson/maven/Maven31ProcessFactory.java b/maven-plugin/src/main/java/hudson/maven/Maven31ProcessFactory.java index e5a4dfb2eb..387e3b0089 100644 --- a/maven-plugin/src/main/java/hudson/maven/Maven31ProcessFactory.java +++ b/maven-plugin/src/main/java/hudson/maven/Maven31ProcessFactory.java @@ -31,6 +31,7 @@ import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.tasks.Maven.MavenInstallation; import jenkins.maven3.agent.Maven31Main; +import jenkins.security.MasterToSlave; import org.jvnet.hudson.maven3.launcher.Maven31Interceptor; import org.jvnet.hudson.maven3.listeners.HudsonMavenExecutionResult; @@ -73,6 +74,7 @@ public class Maven31ProcessFactory extends Maven3ProcessFactory channel.call(new InstallPlexusModulesTask(context)); } + @MasterToSlave private static final class InstallPlexusModulesTask implements Callable { PlexusModuleContributor c; diff --git a/maven-plugin/src/main/java/hudson/maven/Maven3Builder.java b/maven-plugin/src/main/java/hudson/maven/Maven3Builder.java index a87449644e..d5d5a31a2e 100644 --- a/maven-plugin/src/main/java/hudson/maven/Maven3Builder.java +++ b/maven-plugin/src/main/java/hudson/maven/Maven3Builder.java @@ -58,7 +58,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; -import static hudson.Util.fixNull; +import static hudson.Util.*; /** * @author Olivier Lamy diff --git a/maven-plugin/src/main/java/hudson/maven/Maven3ProcessFactory.java b/maven-plugin/src/main/java/hudson/maven/Maven3ProcessFactory.java index 61d1a69dcb..223fcc2947 100644 --- a/maven-plugin/src/main/java/hudson/maven/Maven3ProcessFactory.java +++ b/maven-plugin/src/main/java/hudson/maven/Maven3ProcessFactory.java @@ -32,6 +32,7 @@ import hudson.model.TaskListener; import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.tasks.Maven.MavenInstallation; +import jenkins.security.MasterToSlave; import org.jvnet.hudson.maven3.agent.Maven3Main; import org.jvnet.hudson.maven3.launcher.Maven3Launcher; import org.jvnet.hudson.maven3.listeners.HudsonMavenExecutionResult; @@ -106,6 +107,7 @@ public class Maven3ProcessFactory extends AbstractMavenProcessFactory implements /** * Finds classworlds.jar */ + @MasterToSlave protected static final class GetClassWorldsJar implements Callable { private static final long serialVersionUID = -2599434124883557137L; private final String mvnHome; diff --git a/maven-plugin/src/main/java/hudson/maven/MavenBuildProxy.java b/maven-plugin/src/main/java/hudson/maven/MavenBuildProxy.java index 3e15a6d4f9..e13973f5e5 100644 --- a/maven-plugin/src/main/java/hudson/maven/MavenBuildProxy.java +++ b/maven-plugin/src/main/java/hudson/maven/MavenBuildProxy.java @@ -33,6 +33,7 @@ import java.io.Serializable; import java.util.Calendar; import java.util.List; import jenkins.model.ArtifactManager; +import jenkins.security.SlaveToMaster; /** * Remoting proxy interface for {@link MavenReporter}s to talk to {@link MavenBuild} @@ -266,6 +267,7 @@ public interface MavenBuildProxy { /** * {@link Callable} for invoking {@link BuildCallable} asynchronously. */ + @SlaveToMaster protected static final class AsyncInvoker implements DelegatingCallable { private final MavenBuildProxy proxy; private final BuildCallable program; diff --git a/maven-plugin/src/main/java/hudson/maven/MavenBuilder.java b/maven-plugin/src/main/java/hudson/maven/MavenBuilder.java index 72a60954d9..2ff9180106 100644 --- a/maven-plugin/src/main/java/hudson/maven/MavenBuilder.java +++ b/maven-plugin/src/main/java/hudson/maven/MavenBuilder.java @@ -34,18 +34,6 @@ import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.remoting.DelegatingCallable; import hudson.util.IOException2; - -import java.io.IOException; -import java.io.PrintStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.text.NumberFormat; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; - import org.apache.maven.BuildFailureException; import org.apache.maven.execution.MavenSession; import org.apache.maven.execution.ReactorManager; @@ -60,6 +48,17 @@ import org.codehaus.classworlds.NoSuchRealmException; import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; import org.codehaus.plexus.configuration.PlexusConfiguration; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.text.NumberFormat; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; + /** * {@link Callable} that invokes Maven CLI (in process) and drives a build. * diff --git a/maven-plugin/src/main/java/hudson/maven/MavenProcessFactory.java b/maven-plugin/src/main/java/hudson/maven/MavenProcessFactory.java index 41fe5abd42..895435a8d4 100644 --- a/maven-plugin/src/main/java/hudson/maven/MavenProcessFactory.java +++ b/maven-plugin/src/main/java/hudson/maven/MavenProcessFactory.java @@ -34,6 +34,7 @@ import hudson.model.TaskListener; import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.tasks.Maven.MavenInstallation; +import jenkins.security.MasterToSlave; import java.io.File; import java.io.FilenameFilter; @@ -110,6 +111,7 @@ final class MavenProcessFactory extends AbstractMavenProcessFactory implements P channel.call(new InstallPlexusModulesTask(context)); } + @MasterToSlave private static final class InstallPlexusModulesTask implements Callable { PlexusModuleContributor c; @@ -126,6 +128,7 @@ final class MavenProcessFactory extends AbstractMavenProcessFactory implements P /** * Finds classworlds.jar */ + @MasterToSlave private static final class GetClassWorldsJar implements Callable { private static final long serialVersionUID = 5812919424079344101L; private final String mvnHome; diff --git a/maven-plugin/src/main/java/hudson/maven/MavenVersionCallable.java b/maven-plugin/src/main/java/hudson/maven/MavenVersionCallable.java index f4a0f55cf1..80ffb81032 100644 --- a/maven-plugin/src/main/java/hudson/maven/MavenVersionCallable.java +++ b/maven-plugin/src/main/java/hudson/maven/MavenVersionCallable.java @@ -30,14 +30,15 @@ import hudson.remoting.Callable; import java.io.File; import java.io.IOException; +import jenkins.security.MasterToSlave; import org.kohsuke.stapler.framework.io.IOException2; /** * * @author Olivier Lamy * @since 3.0 - * */ +@MasterToSlave public class MavenVersionCallable implements Callable { diff --git a/maven-plugin/src/main/java/hudson/maven/ProcessCache.java b/maven-plugin/src/main/java/hudson/maven/ProcessCache.java index 165a7aa68a..f61bf4643a 100644 --- a/maven-plugin/src/main/java/hudson/maven/ProcessCache.java +++ b/maven-plugin/src/main/java/hudson/maven/ProcessCache.java @@ -35,6 +35,8 @@ import hudson.remoting.RequestAbortedException; import hudson.tasks.Maven.MavenInstallation; import hudson.util.DelegatingOutputStream; import hudson.util.NullStream; +import jenkins.security.MasterToSlave; +import jenkins.security.SlaveToMaster; import java.io.IOException; import java.io.OutputStream; @@ -248,6 +250,7 @@ public final class ProcessCache { /** * Noop callable used for checking the sanity of the maven process in the cache. */ + @MasterToSlave private static class SetSystemProperties implements Callable { private final Properties properties; @@ -262,6 +265,7 @@ public final class ProcessCache { private static final long serialVersionUID = 1L; } + @MasterToSlave private static class GetSystemProperties implements Callable { public Properties call() { return System.getProperties(); diff --git a/maven-plugin/src/main/java/hudson/maven/RedeployPublisher.java b/maven-plugin/src/main/java/hudson/maven/RedeployPublisher.java index 5d0ef7ffe6..8440f80bc7 100644 --- a/maven-plugin/src/main/java/hudson/maven/RedeployPublisher.java +++ b/maven-plugin/src/main/java/hudson/maven/RedeployPublisher.java @@ -53,6 +53,7 @@ import java.util.Properties; import jenkins.model.Jenkins; import jenkins.mvn.GlobalSettingsProvider; import jenkins.mvn.SettingsProvider; +import jenkins.security.MasterToSlave; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; @@ -286,6 +287,7 @@ public class RedeployPublisher extends Recorder { } } + @MasterToSlave private static final class GetUserHome implements Callable { private static final long serialVersionUID = -8755705771716056636L; diff --git a/maven-plugin/src/main/java/hudson/maven/SplittableBuildListener.java b/maven-plugin/src/main/java/hudson/maven/SplittableBuildListener.java index e17dc74b86..416a503592 100644 --- a/maven-plugin/src/main/java/hudson/maven/SplittableBuildListener.java +++ b/maven-plugin/src/main/java/hudson/maven/SplittableBuildListener.java @@ -31,6 +31,7 @@ import hudson.model.StreamBuildListener; import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.util.AbstractTaskListener; +import jenkins.security.MasterToSlave; import jenkins.util.MarkFindingOutputStream; import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.commons.io.output.DeferredFileOutputStream; @@ -233,6 +234,7 @@ final class SplittableBuildListener extends AbstractTaskListener implements Buil } } + @MasterToSlave private static class SendMark implements Callable { public Void call() throws IOException { // write a mark diff --git a/test/src/test/java/hudson/slaves/JNLPLauncherTest.java b/test/src/test/java/hudson/slaves/JNLPLauncherTest.java index ab9afba1ee..8d1e6bf7f4 100644 --- a/test/src/test/java/hudson/slaves/JNLPLauncherTest.java +++ b/test/src/test/java/hudson/slaves/JNLPLauncherTest.java @@ -35,6 +35,8 @@ import hudson.remoting.Callable; import hudson.remoting.Which; import hudson.util.ArgumentListBuilder; +import jenkins.security.MasterToSlave; +import jenkins.security.SlaveToMaster; import org.jvnet.hudson.test.HudsonTestCase; import org.jvnet.hudson.test.TestExtension; @@ -154,6 +156,7 @@ public class JNLPLauncherTest extends HudsonTestCase { return c; } + @MasterToSlave @SlaveToMaster private static class NoopTask implements Callable { public String call() { return "done"; From 42b36e460dff815ba78dfdab9ef7b1eded12a4c2 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Thu, 18 Sep 2014 15:14:55 -0700 Subject: [PATCH 09/54] Allow hudson.remoting.* callables that are used to implement lower-level functionalities ... like pipes. IOSyncer, PingThread.Ping, RPCRequest, SetMaximumBytecodeLevel look all harmless. PreloadJarTask is dubious, but with classloading from slave disabled, it becomes harmless. --- .../main/java/jenkins/security/CallableDirectionChecker.java | 3 +++ core/src/main/java/jenkins/security/MasterToSlave.java | 1 + 2 files changed, 4 insertions(+) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index 8fbd68da96..42cfa356ab 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -29,6 +29,9 @@ public class CallableDirectionChecker extends CallableDecorator { public Callable userRequest(Callable op, Callable stem) { Class c = op.getClass(); + if (c.getName().startsWith("hudson.remoting")) + return stem; // lower level services provided by remoting, such IOSyncer, RPCRequest, Ping, etc. that we allow + boolean m2s = c.isAnnotationPresent(MasterToSlave.class); boolean s2m = c.isAnnotationPresent(SlaveToMaster.class); diff --git a/core/src/main/java/jenkins/security/MasterToSlave.java b/core/src/main/java/jenkins/security/MasterToSlave.java index 832af307a3..c5b03b6c70 100644 --- a/core/src/main/java/jenkins/security/MasterToSlave.java +++ b/core/src/main/java/jenkins/security/MasterToSlave.java @@ -15,6 +15,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; * * @author Kohsuke Kawaguchi * @see SlaveToMaster + * @see CallableDirectionChecker * @since 1.THU */ @Retention(RUNTIME) From e9eb408e6fddc810629632ea8fe54154207feafa Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Thu, 18 Sep 2014 15:49:53 -0700 Subject: [PATCH 10/54] A merge failure in 1239ed3b If you look at the log graph of this file, you'll see that 535c111 removed this line for SECURITY-76, but it's still present in 1239ed3b. Most likely because d482fc50 edited a nearby line, and the merge conflict wasn't properly resolved. --- core/src/main/java/hudson/markup/MyspacePolicy.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/java/hudson/markup/MyspacePolicy.java b/core/src/main/java/hudson/markup/MyspacePolicy.java index dc9e0ea20c..507acd12a5 100644 --- a/core/src/main/java/hudson/markup/MyspacePolicy.java +++ b/core/src/main/java/hudson/markup/MyspacePolicy.java @@ -66,7 +66,6 @@ public class MyspacePolicy { tag("span,div"); tag("img", "src",ONSITE_OR_OFFSITE_URL, "hspace","vspace"); - tag("iframe", "src"); tag("ul,ol,li,dd,dl,dt,thead,tbody,tfoot"); tag("table", "noresize"); tag("td,th,tr"); From 3e15684f75bb80c0e138052ca71033ce6d6b58cd Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Thu, 18 Sep 2014 15:50:01 -0700 Subject: [PATCH 11/54] This is failing the animal sniffer test --- core/src/main/java/hudson/Plugin.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/Plugin.java b/core/src/main/java/hudson/Plugin.java index 69c6118d12..934b87166a 100644 --- a/core/src/main/java/hudson/Plugin.java +++ b/core/src/main/java/hudson/Plugin.java @@ -23,6 +23,7 @@ */ package hudson; +import hudson.util.IOException2; import hudson.util.TimeUnit2; import jenkins.model.Jenkins; import hudson.model.Descriptor; @@ -220,7 +221,7 @@ public abstract class Plugin implements Saveable { try { rsp.serveLocalizedFile(req, wrapper.baseResourceURL.toURI().resolve(new URI(null, '.' + path, null)).toURL(), expires); } catch (URISyntaxException x) { - throw new IOException(x); + throw new IOException2(x); } } From e4a1ac2ba18993c10b6b6a858f07327e93a55355 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Thu, 18 Sep 2014 18:40:56 -0700 Subject: [PATCH 12/54] Added a hook to intercept remote file access and reject them if necessary --- core/src/main/java/hudson/FilePath.java | 281 ++++++++++++------ .../src/main/java/jenkins/FilePathFilter.java | 73 +++++ .../jenkins/FilePathFilterAggregator.java | 49 +++ 3 files changed, 307 insertions(+), 96 deletions(-) create mode 100644 core/src/main/java/jenkins/FilePathFilter.java create mode 100644 core/src/main/java/jenkins/FilePathFilterAggregator.java diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index f6e01374ac..7256c19fea 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -25,45 +25,51 @@ */ package hudson; +import com.jcraft.jzlib.GZIPInputStream; +import com.jcraft.jzlib.GZIPOutputStream; +import com.sun.jna.Native; import hudson.Launcher.LocalLauncher; import hudson.Launcher.RemoteLauncher; -import jenkins.model.Jenkins; -import hudson.model.TaskListener; import hudson.model.AbstractProject; import hudson.model.Item; +import hudson.model.TaskListener; +import hudson.org.apache.tools.tar.TarInputStream; +import hudson.os.PosixException; import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.remoting.DelegatingCallable; import hudson.remoting.Future; import hudson.remoting.Pipe; +import hudson.remoting.RemoteInputStream; import hudson.remoting.RemoteOutputStream; import hudson.remoting.VirtualChannel; -import hudson.remoting.RemoteInputStream; import hudson.remoting.Which; import hudson.security.AccessControlled; import hudson.util.DirScanner; -import hudson.util.IOException2; -import hudson.util.HeadBufferingStream; +import hudson.util.FileVisitor; import hudson.util.FormValidation; +import hudson.util.HeadBufferingStream; +import hudson.util.IOException2; import hudson.util.IOUtils; - -import static hudson.Util.*; -import static hudson.util.jna.GNUCLibrary.LIBC; -import static hudson.FilePath.TarCompression.GZIP; -import hudson.org.apache.tools.tar.TarInputStream; import hudson.util.io.Archiver; import hudson.util.io.ArchiverFactory; +import jenkins.FilePathFilter; +import jenkins.model.Jenkins; import jenkins.security.MasterToSlave; import jenkins.security.MasterToSlaveCallable; import jenkins.security.SlaveToMaster; import jenkins.util.VirtualFile; +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.io.input.CountingInputStream; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Chmod; import org.apache.tools.ant.types.FileSet; import org.apache.tools.tar.TarEntry; -import org.apache.commons.io.input.CountingInputStream; -import org.apache.commons.fileupload.FileItem; +import org.apache.tools.zip.ZipEntry; +import org.apache.tools.zip.ZipFile; import org.kohsuke.stapler.Stapler; + import java.io.BufferedOutputStream; import java.io.File; import java.io.FileFilter; @@ -76,39 +82,33 @@ import java.io.InterruptedIOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.io.Serializable; import java.io.Writer; -import java.io.OutputStreamWriter; import java.lang.reflect.Field; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; -import java.util.List; -import java.util.StringTokenizer; import java.util.Arrays; import java.util.Comparator; -import java.util.logging.Level; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import com.jcraft.jzlib.GZIPInputStream; -import com.jcraft.jzlib.GZIPOutputStream; - -import com.sun.jna.Native; -import hudson.os.PosixException; -import hudson.util.FileVisitor; -import java.util.Enumeration; -import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; import java.util.logging.Logger; -import org.apache.tools.ant.taskdefs.Chmod; +import java.util.regex.Matcher; +import java.util.regex.Pattern; -import org.apache.tools.zip.ZipFile; -import org.apache.tools.zip.ZipEntry; +import static hudson.FilePath.TarCompression.*; +import static hudson.Util.*; +import static hudson.util.jna.GNUCLibrary.*; /** * {@link File} like object with remoting support. @@ -423,7 +423,7 @@ public final class FilePath implements Serializable { public Integer invoke(File f, VirtualChannel channel) throws IOException { Archiver a = factory.create(out); try { - scanner.scan(f,a); + scanner.scan(f,reading(a)); } finally { a.close(); } @@ -457,7 +457,7 @@ public final class FilePath implements Serializable { if (FilePath.this.isRemote()) unzip(dir, FilePath.this.read()); // use streams else - unzip(dir, new File(FilePath.this.getRemote())); // shortcut to local file + unzip(dir, reading(new File(FilePath.this.getRemote()))); // shortcut to local file return null; } private static final long serialVersionUID = 1L; @@ -526,13 +526,13 @@ public final class FilePath implements Serializable { ZipEntry e = entries.nextElement(); File f = new File(dir, e.getName()); if (e.isDirectory()) { - f.mkdirs(); + mkdirs(f); } else { File p = f.getParentFile(); if (p != null) { - p.mkdirs(); + mkdirs(f); } - IOUtils.copy(zip.getInputStream(e), f); + IOUtils.copy(zip.getInputStream(e), writing(f)); try { FilePath target = new FilePath(f); int mode = e.getUnixMode(); @@ -574,6 +574,7 @@ public final class FilePath implements Serializable { act(new FileCallable() { private static final long serialVersionUID = 1L; public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { + writing(f); Util.createSymlink(f.getParentFile(),target,f.getName(),listener); return null; } @@ -591,7 +592,7 @@ public final class FilePath implements Serializable { return act(new FileCallable() { private static final long serialVersionUID = 1L; public String invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { - return Util.resolveSymlink(f); + return Util.resolveSymlink(reading(f)); } }); } @@ -772,6 +773,7 @@ public final class FilePath implements Serializable { } } + @MasterToSlave // this reads from arbitrary URL private static final class Unpack implements FileCallable { private final URL archive; Unpack(URL archive) { @@ -841,7 +843,7 @@ public final class FilePath implements Serializable { public void copyFrom(FileItem file) throws IOException, InterruptedException { if(channel==null) { try { - file.write(new File(remote)); + file.write(writing(new File(remote))); } catch (IOException e) { throw e; } catch (Exception e) { @@ -1062,7 +1064,7 @@ public final class FilePath implements Serializable { if(!act(new FileCallable() { private static final long serialVersionUID = 1L; public Boolean invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { - if(f.mkdirs() || f.exists()) + if(mkdirs(f) || f.exists()) return true; // OK // following Ant task to avoid possible race condition. @@ -1081,7 +1083,7 @@ public final class FilePath implements Serializable { act(new FileCallable() { private static final long serialVersionUID = 1L; public Void invoke(File f, VirtualChannel channel) throws IOException { - Util.deleteRecursive(f); + deleteRecursive(deleting(f)); return null; } }); @@ -1094,12 +1096,35 @@ public final class FilePath implements Serializable { act(new FileCallable() { private static final long serialVersionUID = 1L; public Void invoke(File f, VirtualChannel channel) throws IOException { - Util.deleteContentsRecursive(f); + deleteContentsRecursive(f); return null; } }); } + private void deleteRecursive(File dir) throws IOException { + if(!isSymlink(dir)) + deleteContentsRecursive(dir); + try { + deleteFile(deleting(dir)); + } catch (IOException e) { + // if some of the child directories are big, it might take long enough to delete that + // it allows others to create new files, causing problemsl ike JENKINS-10113 + // so give it one more attempt before we give up. + if(!isSymlink(dir)) + deleteContentsRecursive(dir); + deleteFile(deleting(dir)); + } + } + + private void deleteContentsRecursive(File file) throws IOException { + File[] files = file.listFiles(); + if(files==null) + return; // the directory didn't exist in the first place + for (File child : files) + deleteRecursive(child); + } + /** * Gets the file name portion except the extension. * @@ -1188,7 +1213,7 @@ public final class FilePath implements Serializable { return new FilePath(this,act(new FileCallable() { private static final long serialVersionUID = 1L; public String invoke(File dir, VirtualChannel channel) throws IOException { - File f = File.createTempFile(prefix, suffix, dir); + File f = writing(File.createTempFile(prefix, suffix, dir)); return f.getName(); } })); @@ -1247,16 +1272,16 @@ public final class FilePath implements Serializable { if(!inThisDirectory) dir = new File(System.getProperty("java.io.tmpdir")); else - dir.mkdirs(); + mkdirs(dir); File f; try { - f = File.createTempFile(prefix, suffix, dir); + f = creating(File.createTempFile(prefix, suffix, dir)); } catch (IOException e) { throw new IOException2("Failed to create a temporary directory in "+dir,e); } - Writer w = new FileWriter(f); + Writer w = new FileWriter(writing(f)); try { w.write(contents); } finally { @@ -1310,7 +1335,7 @@ public final class FilePath implements Serializable { act(new FileCallable() { private static final long serialVersionUID = 1L; public Void invoke(File f, VirtualChannel channel) throws IOException { - Util.deleteFile(f); + Util.deleteFile(deleting(f)); return null; } }); @@ -1324,7 +1349,7 @@ public final class FilePath implements Serializable { return act(new FileCallable() { private static final long serialVersionUID = 1L; public Boolean invoke(File f, VirtualChannel channel) throws IOException { - return f.exists(); + return stating(f).exists(); } }); } @@ -1340,7 +1365,7 @@ public final class FilePath implements Serializable { return act(new FileCallable() { private static final long serialVersionUID = 1L; public Long invoke(File f, VirtualChannel channel) throws IOException { - return f.lastModified(); + return stating(f).lastModified(); } }); } @@ -1355,8 +1380,8 @@ public final class FilePath implements Serializable { private static final long serialVersionUID = -5094638816500738429L; public Void invoke(File f, VirtualChannel channel) throws IOException { if(!f.exists()) - new FileOutputStream(f).close(); - if(!f.setLastModified(timestamp)) + new FileOutputStream(creating(f)).close(); + if(!stating(f).setLastModified(timestamp)) throw new IOException("Failed to set the timestamp of "+f+" to "+timestamp); return null; } @@ -1367,7 +1392,7 @@ public final class FilePath implements Serializable { String message = act(new FileCallable() { private static final long serialVersionUID = -828220335793641630L; public String invoke(File f, VirtualChannel channel) throws IOException { - if(!f.setLastModified(timestamp)) { + if(!writing(f).setLastModified(timestamp)) { if (Functions.isWindows()) { // On Windows this seems to fail often. See JENKINS-11073 // Therefore don't fail, but just log a warning @@ -1392,7 +1417,7 @@ public final class FilePath implements Serializable { return act(new FileCallable() { private static final long serialVersionUID = 1L; public Boolean invoke(File f, VirtualChannel channel) throws IOException { - return f.isDirectory(); + return stating(f).isDirectory(); } }); } @@ -1406,7 +1431,7 @@ public final class FilePath implements Serializable { return act(new FileCallable() { private static final long serialVersionUID = 1L; public Long invoke(File f, VirtualChannel channel) throws IOException { - return f.length(); + return stating(f).length(); } }); } @@ -1431,7 +1456,7 @@ public final class FilePath implements Serializable { act(new FileCallable() { private static final long serialVersionUID = 1L; public Void invoke(File f, VirtualChannel channel) throws IOException { - _chmod(f, mask); + _chmod(writing(f), mask); return null; } @@ -1482,7 +1507,7 @@ public final class FilePath implements Serializable { return act(new FileCallable() { private static final long serialVersionUID = 1L; public Integer invoke(File f, VirtualChannel channel) throws IOException { - return IOUtils.mode(f); + return IOUtils.mode(stating(f)); } }); } @@ -1529,7 +1554,7 @@ public final class FilePath implements Serializable { return act(new FileCallable>() { private static final long serialVersionUID = 1L; public List invoke(File f, VirtualChannel channel) throws IOException { - File[] children = f.listFiles(filter); + File[] children = reading(f).listFiles(filter); if(children ==null) return null; ArrayList r = new ArrayList(children.length); @@ -1582,7 +1607,7 @@ public final class FilePath implements Serializable { return act(new FileCallable() { private static final long serialVersionUID = 1L; public FilePath[] invoke(File f, VirtualChannel channel) throws IOException { - String[] files = glob(f, includes, excludes, defaultExcludes); + String[] files = glob(reading(f), includes, excludes, defaultExcludes); FilePath[] r = new FilePath[files.length]; for( int i=0; i() { @@ -1622,7 +1647,7 @@ public final class FilePath implements Serializable { public Void call() throws IOException { FileInputStream fis=null; try { - fis = new FileInputStream(new File(remote)); + fis = new FileInputStream(reading(new File(remote))); Util.copyStream(fis,p.getOut()); return null; } finally { @@ -1665,16 +1690,16 @@ public final class FilePath implements Serializable { public OutputStream write() throws IOException, InterruptedException { if(channel==null) { File f = new File(remote).getAbsoluteFile(); - f.getParentFile().mkdirs(); - return new FileOutputStream(f); + mkdirs(f.getParentFile()); + return new FileOutputStream(writing(f)); } return channel.call(new Callable() { private static final long serialVersionUID = 1L; public OutputStream call() throws IOException { File f = new File(remote).getAbsoluteFile(); - f.getParentFile().mkdirs(); - FileOutputStream fos = new FileOutputStream(f); + mkdirs(f.getParentFile()); + FileOutputStream fos = new FileOutputStream(writing(f)); return new RemoteOutputStream(fos); } }); @@ -1691,8 +1716,8 @@ public final class FilePath implements Serializable { act(new FileCallable() { private static final long serialVersionUID = 1L; public Void invoke(File f, VirtualChannel channel) throws IOException { - f.getParentFile().mkdirs(); - FileOutputStream fos = new FileOutputStream(f); + mkdirs(f.getParentFile()); + FileOutputStream fos = new FileOutputStream(writing(f)); Writer w = encoding != null ? new OutputStreamWriter(fos, encoding) : new OutputStreamWriter(fos); try { w.write(content); @@ -1712,7 +1737,7 @@ public final class FilePath implements Serializable { return act(new FileCallable() { private static final long serialVersionUID = 1L; public String invoke(File f, VirtualChannel channel) throws IOException { - return Util.getDigestOf(f); + return Util.getDigestOf(reading(f)); } }); } @@ -1728,7 +1753,7 @@ public final class FilePath implements Serializable { act(new FileCallable() { private static final long serialVersionUID = 1L; public Void invoke(File f, VirtualChannel channel) throws IOException { - f.renameTo(new File(target.remote)); + reading(f).renameTo(creating(new File(target.remote))); return null; } }); @@ -1748,12 +1773,12 @@ public final class FilePath implements Serializable { public Void invoke(File f, VirtualChannel channel) throws IOException { File t = new File(target.getRemote()); - for(File child : f.listFiles()) { + for(File child : reading(f).listFiles()) { File target = new File(t, child.getName()); - if(!child.renameTo(target)) + if(!stating(child).renameTo(creating(target))) throw new IOException("Failed to rename "+child+" to "+target); } - f.delete(); + deleting(f).delete(); return null; } }); @@ -1797,7 +1822,7 @@ public final class FilePath implements Serializable { public Void invoke(File f, VirtualChannel channel) throws IOException { FileInputStream fis = null; try { - fis = new FileInputStream(f); + fis = new FileInputStream(reading(f)); Util.copyStream(fis,out); return null; } finally { @@ -1839,21 +1864,6 @@ public final class FilePath implements Serializable { channel.syncLocalIO(); } - /** - * Remoting interface used for {@link FilePath#copyRecursiveTo(String, FilePath)}. - * - * TODO: this might not be the most efficient way to do the copy. - */ - interface RemoteCopier { - /** - * @param fileName - * relative path name to the output file. Path separator must be '/'. - */ - void open(String fileName) throws IOException; - void write(byte[] buf, int len) throws IOException; - void close() throws IOException; - } - /** * Copies the contents of this directory recursively into the specified target directory. * @@ -1915,27 +1925,33 @@ public final class FilePath implements Serializable { assert target.channel==null; final File dest = new File(target.remote); final AtomicInteger count = new AtomicInteger(); - scanner.scan(base, new FileVisitor() { - @Override public void visit(File f, String relativePath) throws IOException { + scanner.scan(base, reading(new FileVisitor() { + @Override + public void visit(File f, String relativePath) throws IOException { if (f.isFile()) { File target = new File(dest, relativePath); - target.getParentFile().mkdirs(); - Util.copyFile(f, target); + mkdirs(target.getParentFile()); + Util.copyFile(f, writing(target)); count.incrementAndGet(); } } - @Override public boolean understandsSymlink() { + + @Override + public boolean understandsSymlink() { return true; } - @Override public void visitSymlink(File link, String target, String relativePath) throws IOException { + + @Override + public void visitSymlink(File link, String target, String relativePath) throws IOException { try { + writing(new File(dest, target)); Util.createSymlink(dest, target, relativePath, TaskListener.NULL); } catch (InterruptedException x) { throw (IOException) new IOException(x.toString()).initCause(x); } count.incrementAndGet(); } - }); + })); return count.get(); } }); @@ -2035,7 +2051,7 @@ public final class FilePath implements Serializable { private static Integer writeToTar(File baseDir, DirScanner scanner, OutputStream out) throws IOException { Archiver tw = ArchiverFactory.TAR.create(out); try { - scanner.scan(baseDir,tw); + scanner.scan(baseDir,reading(tw)); } finally { tw.close(); } @@ -2052,10 +2068,11 @@ public final class FilePath implements Serializable { while ((te = t.getNextEntry()) != null) { File f = new File(baseDir,te.getName()); if(te.isDirectory()) { - f.mkdirs(); + mkdirs(f); } else { File parent = f.getParentFile(); - if (parent != null) parent.mkdirs(); + if (parent != null) mkdirs(parent); + writing(f); byte linkFlag = (Byte) LINKFLAG_FIELD.get(te); if (linkFlag==TarEntry.LF_SYMLINK) { @@ -2173,7 +2190,7 @@ public final class FilePath implements Serializable { {// check the (2) above next as this is more expensive. // Try prepending "**/" to see if that results in a match - FileSet fs = Util.createFileSet(dir,"**/"+fileMask); + FileSet fs = Util.createFileSet(reading(dir),"**/"+fileMask); DirectoryScanner ds = fs.getDirectoryScanner(new Project()); if(ds.getIncludedFilesCount()!=0) { // try shorter name first so that the suggestion results in least amount of changes @@ -2242,7 +2259,7 @@ public final class FilePath implements Serializable { return super.isCaseSensitive(); } }; - ds.setBasedir(dir); + ds.setBasedir(reading(dir)); ds.setIncludes(new String[] {pattern}); try { ds.scan(); @@ -2530,4 +2547,76 @@ public final class FilePath implements Serializable { } + /** + * Wraps {@link FileVisitor} to notify read access to {@link FilePathFilter}. + */ + private static FileVisitor reading(final FileVisitor v) { + final FilePathFilter filter = FilePathFilter.current(); + if (filter==null) return v; + + return new FileVisitor() { + @Override + public void visit(File f, String relativePath) throws IOException { + filter.read(f); + v.visit(f,relativePath); + } + + @Override + public void visitSymlink(File link, String target, String relativePath) throws IOException { + filter.readSymlink(link); + v.visitSymlink(link, target, relativePath); + } + + @Override + public boolean understandsSymlink() { + return true; + } + }; + } + + /** + * Pass through 'f' after ensuring that we can read that file. + */ + private static File reading(File f) { + FilePathFilter.currentNonnull().read(f); + return f; + } + + /** + * Pass through 'f' after ensuring that we can access the file attributes. + */ + private static File stating(File f) { + FilePathFilter.currentNonnull().stat(f); + return f; + } + + /** + * Pass through 'f' after ensuring that we can create that file/dir. + */ + private static File creating(File f) { + FilePathFilter.currentNonnull().create(f); + return f; + } + + /** + * Pass through 'f' after ensuring that we can write to that file. + */ + private static File writing(File f) { + FilePathFilter.currentNonnull().write(f); + return f; + } + + /** + * Pass through 'f' after ensuring that we can write to that file. + */ + private static File deleting(File f) { + FilePathFilter.currentNonnull().delete(f); + return f; + } + + + private static boolean mkdirs(File dir) { + FilePathFilter.currentNonnull().mkdirs(dir); + return dir.mkdirs(); + } } diff --git a/core/src/main/java/jenkins/FilePathFilter.java b/core/src/main/java/jenkins/FilePathFilter.java new file mode 100644 index 0000000000..b37875ba80 --- /dev/null +++ b/core/src/main/java/jenkins/FilePathFilter.java @@ -0,0 +1,73 @@ +package jenkins; + +import hudson.FilePath; +import hudson.remoting.Channel; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import java.io.File; + +/** + * Inspects {@link FilePath} access from remote channels. + * + * @author Kohsuke Kawaguchi + * @see FilePath + * @since 1.THU + */ +public abstract class FilePathFilter { + public void read(File f) throws SecurityException {} + public void readSymlink(File f) throws SecurityException {} + public void write(File f) throws SecurityException {} + public void mkdirs(File f) throws SecurityException {} + public void create(File f) throws SecurityException {} + public void delete(File f) throws SecurityException {} + public void stat(File f) throws SecurityException {} + + + public void installTo(Channel ch) { + synchronized (ch) { + FilePathFilterAggregator filters = ch.getProperty(FilePathFilterAggregator.KEY); + if (filters==null) { + filters = new FilePathFilterAggregator(); + ch.setProperty(FilePathFilterAggregator.KEY,filters); + } + filters.add(this); + } + } + + public void uninstallFrom(Channel ch) { + synchronized (ch) { + FilePathFilterAggregator filters = ch.getProperty(FilePathFilterAggregator.KEY); + if (filters!=null) { + filters.remove(this); + } + } + } + + /** + * Returns an {@link FilePathFilter} object that represents all the in-scope filters, + * or null if none is needed. + */ + public static @CheckForNull FilePathFilter current() { + Channel ch = Channel.current(); + if (ch==null) return null; + + return ch.getProperty(FilePathFilterAggregator.KEY); + } + + /** + * A flavor of {@link #current()} that returns a place holder empty object if none is needed. + * + * @return + * never null. If no filter is in scope, this method returns a non-null object that + * does nothing. + */ + public static @Nonnull FilePathFilter currentNonnull() { + FilePathFilter f = current(); + if (f==null) return NOOP; + return f; + } + + private static final FilePathFilter NOOP = new FilePathFilter() { + }; +} diff --git a/core/src/main/java/jenkins/FilePathFilterAggregator.java b/core/src/main/java/jenkins/FilePathFilterAggregator.java new file mode 100644 index 0000000000..8318303eba --- /dev/null +++ b/core/src/main/java/jenkins/FilePathFilterAggregator.java @@ -0,0 +1,49 @@ +package jenkins; + +import hudson.FilePath; +import hudson.remoting.ChannelProperty; + +import java.io.File; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Maintains a bundle of {@link FilePathFilter} and implement a hook that broadcasts to all the filters. + * + * Accessible as channel property. + * + * @author Kohsuke Kawaguchi + * @see FilePath + * @since 1.THU + */ +class FilePathFilterAggregator extends FilePathFilter { + private final CopyOnWriteArrayList all = new CopyOnWriteArrayList(); + + public void add(FilePathFilter f) { + all.add(f); + } + + public void remove(FilePathFilter f) { + all.remove(f); + } + + public void read(File f) throws SecurityException { + for (FilePathFilter filter : all) { + filter.read(f); + } + } + + public void write(File f) throws SecurityException { + throw new UnsupportedOperationException(); + } + public void create(File f) throws SecurityException { + throw new UnsupportedOperationException(); + } + public void delete(File f) throws SecurityException { + throw new UnsupportedOperationException(); + } + public void stat(File f) throws SecurityException { + throw new UnsupportedOperationException(); + } + + static final ChannelProperty KEY = new ChannelProperty(FilePathFilterAggregator.class, "FilePathFilters"); +} From 7bd7e4f0b26a7a339016a3c678db50a1b2f423bd Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Thu, 18 Sep 2014 18:52:05 -0700 Subject: [PATCH 13/54] Fix up. - If a write results in a file creation, do the create check, to align with the POSIX semantics. - Don't make pointless mkdirs check if the directory already exists - read vs readSymlink difference is dangerous because read() can be called for symlinks in other context as well. --- core/src/main/java/hudson/FilePath.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 7256c19fea..f1d9ab637b 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -2563,13 +2563,13 @@ public final class FilePath implements Serializable { @Override public void visitSymlink(File link, String target, String relativePath) throws IOException { - filter.readSymlink(link); + filter.read(link); v.visitSymlink(link, target, relativePath); } @Override public boolean understandsSymlink() { - return true; + return v.understandsSymlink(); } }; } @@ -2602,7 +2602,10 @@ public final class FilePath implements Serializable { * Pass through 'f' after ensuring that we can write to that file. */ private static File writing(File f) { - FilePathFilter.currentNonnull().write(f); + FilePathFilter filter = FilePathFilter.currentNonnull(); + if (!f.exists()) + filter.create(f); + filter.write(f); return f; } @@ -2616,6 +2619,8 @@ public final class FilePath implements Serializable { private static boolean mkdirs(File dir) { + if (dir.exists()) return false; + FilePathFilter.currentNonnull().mkdirs(dir); return dir.mkdirs(); } From 148ad489b157c53fa149a27d3d7742e468ef7918 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Thu, 18 Sep 2014 18:52:15 -0700 Subject: [PATCH 14/54] Filled in the blank of FilePathFilter --- .../src/main/java/jenkins/FilePathFilter.java | 36 ++++++++++++++++++- .../jenkins/FilePathFilterAggregator.java | 23 +++++++++--- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/jenkins/FilePathFilter.java b/core/src/main/java/jenkins/FilePathFilter.java index b37875ba80..991a3a752d 100644 --- a/core/src/main/java/jenkins/FilePathFilter.java +++ b/core/src/main/java/jenkins/FilePathFilter.java @@ -15,12 +15,46 @@ import java.io.File; * @since 1.THU */ public abstract class FilePathFilter { + /** + * Checks if the given file/directory can be read. + * + * On POSIX, this corresponds to the 'r' permission of the file/directory itself. + */ public void read(File f) throws SecurityException {} - public void readSymlink(File f) throws SecurityException {} + + /** + * Checks if the given file can be written. + * + * On POSIX, this corresponds to the 'w' permission of the file itself. + */ public void write(File f) throws SecurityException {} + + /** + * Checks if the given directory can be created. + * + * On POSIX, this corresponds to the 'w' permission of the parent directory. + */ public void mkdirs(File f) throws SecurityException {} + + /** + * Checks if the given file can be created. + * + * On POSIX, this corresponds to the 'w' permission of the parent directory. + */ public void create(File f) throws SecurityException {} + + /** + * Checks if the given file/directory can be deleted. + * + * On POSIX, this corresponds to the 'w' permission of the parent directory. + */ public void delete(File f) throws SecurityException {} + + /** + * Checks if the metadata of the given file/directory (as opposed to the content) can be accessed. + * + * On POSIX, this corresponds to the 'r' permission of the parent directory. + */ public void stat(File f) throws SecurityException {} diff --git a/core/src/main/java/jenkins/FilePathFilterAggregator.java b/core/src/main/java/jenkins/FilePathFilterAggregator.java index 8318303eba..2249b276b2 100644 --- a/core/src/main/java/jenkins/FilePathFilterAggregator.java +++ b/core/src/main/java/jenkins/FilePathFilterAggregator.java @@ -32,17 +32,32 @@ class FilePathFilterAggregator extends FilePathFilter { } } + @Override + public void mkdirs(File f) throws SecurityException { + for (FilePathFilter filter : all) { + filter.mkdirs(f); + } + } + public void write(File f) throws SecurityException { - throw new UnsupportedOperationException(); + for (FilePathFilter filter : all) { + filter.write(f); + } } public void create(File f) throws SecurityException { - throw new UnsupportedOperationException(); + for (FilePathFilter filter : all) { + filter.create(f); + } } public void delete(File f) throws SecurityException { - throw new UnsupportedOperationException(); + for (FilePathFilter filter : all) { + filter.delete(f); + } } public void stat(File f) throws SecurityException { - throw new UnsupportedOperationException(); + for (FilePathFilter filter : all) { + filter.stat(f); + } } static final ChannelProperty KEY = new ChannelProperty(FilePathFilterAggregator.class, "FilePathFilters"); From 3c3c476dbf4a732f743fc2d7edea303f44a51097 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 19 Sep 2014 15:22:24 -0400 Subject: [PATCH 15/54] Comment. --- .../main/java/jenkins/security/CallableDirectionChecker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index 42cfa356ab..9e049c0f85 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -29,7 +29,7 @@ public class CallableDirectionChecker extends CallableDecorator { public Callable userRequest(Callable op, Callable stem) { Class c = op.getClass(); - if (c.getName().startsWith("hudson.remoting")) + if (c.getName().startsWith("hudson.remoting")) // TODO probably insecure return stem; // lower level services provided by remoting, such IOSyncer, RPCRequest, Ping, etc. that we allow boolean m2s = c.isAnnotationPresent(MasterToSlave.class); From 8a7cd18be132540251b4450ffc7ad0b455d27017 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 19 Sep 2014 15:23:30 -0400 Subject: [PATCH 16/54] For now, at least print warnings about unmarked callables. --- .../main/java/jenkins/security/CallableDirectionChecker.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index 9e049c0f85..fea947c6e7 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -39,8 +39,7 @@ public class CallableDirectionChecker extends CallableDecorator { // no annotation provided, so we don't know. // to err on the correctness we'd let it pass with reporting, which // provides auditing trail. - if (LOGGER.isLoggable(Level.FINE)) - LOGGER.fine("Unchecked callable from "+computer.getName()+": "+c); + LOGGER.log(Level.WARNING, "Unchecked callable from {0}: {1}", new Object[] {computer.getName(), c}); return stem; } From 0908362899a6f61bb2475e13f755ea391b133379 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Tue, 29 Oct 2013 09:19:24 -0700 Subject: [PATCH 17/54] Completing the migration to Java6 (cherry picked from commit 5e7b844b65cf021ce1378bc1a1f78f9bc8a2b33d) Conflicts: changelog.html --- pom.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0fed147018..45dc95616d 100644 --- a/pom.xml +++ b/pom.xml @@ -593,8 +593,7 @@ THE SOFTWARE. org.codehaus.mojo.signature - - java15 + java1${java.level} 1.0 From c80741615b27f4414542b7a1aa8999eef2cd3475 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 19 Sep 2014 15:27:05 -0400 Subject: [PATCH 18/54] Revert "This is failing the animal sniffer test" This reverts commit 3e15684f75bb80c0e138052ca71033ce6d6b58cd. Not necessary after getting 5e7b844b6. --- core/src/main/java/hudson/Plugin.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/hudson/Plugin.java b/core/src/main/java/hudson/Plugin.java index 934b87166a..69c6118d12 100644 --- a/core/src/main/java/hudson/Plugin.java +++ b/core/src/main/java/hudson/Plugin.java @@ -23,7 +23,6 @@ */ package hudson; -import hudson.util.IOException2; import hudson.util.TimeUnit2; import jenkins.model.Jenkins; import hudson.model.Descriptor; @@ -221,7 +220,7 @@ public abstract class Plugin implements Saveable { try { rsp.serveLocalizedFile(req, wrapper.baseResourceURL.toURI().resolve(new URI(null, '.' + path, null)).toURL(), expires); } catch (URISyntaxException x) { - throw new IOException2(x); + throw new IOException(x); } } From b1084f8c3817a5e8e4a406713223ad56cc837427 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 19 Sep 2014 15:44:15 -0400 Subject: [PATCH 19/54] Unnecessary override. --- .../java/jenkins/security/CallableDirectionChecker.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index fea947c6e7..e8e3f2c175 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -1,15 +1,12 @@ package jenkins.security; import hudson.Extension; -import hudson.model.Computer; -import hudson.model.TaskListener; import hudson.remoting.Callable; import hudson.remoting.CallableDecorator; import hudson.remoting.ChannelBuilder; import hudson.slaves.ComputerListener; import hudson.slaves.SlaveComputer; -import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; @@ -60,11 +57,6 @@ public class CallableDirectionChecker extends CallableDecorator { public void onChannelBuilding(ChannelBuilder builder, SlaveComputer sc) { builder.with(new CallableDirectionChecker(sc)); } - - @Override - public void preLaunch(Computer c, TaskListener taskListener) throws IOException, InterruptedException { - super.preLaunch(c, taskListener); - } } private static final Logger LOGGER = Logger.getLogger(CallableDirectionChecker.class.getName()); From 1df5ed000d277834bcc2b8d3425a4f72a164644a Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 19 Sep 2014 15:45:10 -0400 Subject: [PATCH 20/54] @since, warnings. --- .../main/java/jenkins/security/CallableDirectionChecker.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index e8e3f2c175..387c019ff2 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -14,6 +14,7 @@ import java.util.logging.Logger; * Rejects {@link MasterToSlave} callables. * * @author Kohsuke Kawaguchi + * @since TODO */ public class CallableDirectionChecker extends CallableDecorator { private final SlaveComputer computer; @@ -24,7 +25,7 @@ public class CallableDirectionChecker extends CallableDecorator { @Override public Callable userRequest(Callable op, Callable stem) { - Class c = op.getClass(); + Class c = op.getClass(); if (c.getName().startsWith("hudson.remoting")) // TODO probably insecure return stem; // lower level services provided by remoting, such IOSyncer, RPCRequest, Ping, etc. that we allow From a42f7667e42851bda5061c124cbf22382201a268 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 19 Sep 2014 15:49:28 -0400 Subject: [PATCH 21/54] Rewriting method body to more legible flow. --- .../security/CallableDirectionChecker.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index 387c019ff2..1dfae3bb76 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -30,23 +30,19 @@ public class CallableDirectionChecker extends CallableDecorator { if (c.getName().startsWith("hudson.remoting")) // TODO probably insecure return stem; // lower level services provided by remoting, such IOSyncer, RPCRequest, Ping, etc. that we allow - boolean m2s = c.isAnnotationPresent(MasterToSlave.class); - boolean s2m = c.isAnnotationPresent(SlaveToMaster.class); + if (c.isAnnotationPresent(SlaveToMaster.class)) { + return stem; // known to be safe + } - if (!m2s && !s2m) { + if (c.isAnnotationPresent(MasterToSlave.class)) { + throw new SecurityException(String.format("Invocation of %s is prohibited", c)); + } else { // no annotation provided, so we don't know. // to err on the correctness we'd let it pass with reporting, which // provides auditing trail. LOGGER.log(Level.WARNING, "Unchecked callable from {0}: {1}", new Object[] {computer.getName(), c}); - return stem; } - - if (s2m) - return stem; // known to be safe - - // this means m2s && !s2m, meaning it's for master->slave - throw new SecurityException(String.format("Invocation of %s is prohibited", c)); } /** From f7840f428f0ae3aac688858228bea980bc1846a6 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 19 Sep 2014 16:09:42 -0400 Subject: [PATCH 22/54] Enforcing callable checking by default, with system properties to selectively or globally disable. --- .../security/CallableDirectionChecker.java | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index 1dfae3bb76..cfedb261fe 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -17,6 +17,9 @@ import java.util.logging.Logger; * @since TODO */ public class CallableDirectionChecker extends CallableDecorator { + + private static final String BYPASS_PROP = "jenkins.security.CallableDirectionChecker.allowUnmarkedCallables"; + private final SlaveComputer computer; public CallableDirectionChecker(SlaveComputer computer) { @@ -26,22 +29,30 @@ public class CallableDirectionChecker extends CallableDecorator { @Override public Callable userRequest(Callable op, Callable stem) { Class c = op.getClass(); + String name = c.getName(); - if (c.getName().startsWith("hudson.remoting")) // TODO probably insecure + if (name.startsWith("hudson.remoting")) { // TODO probably insecure return stem; // lower level services provided by remoting, such IOSyncer, RPCRequest, Ping, etc. that we allow + } if (c.isAnnotationPresent(SlaveToMaster.class)) { return stem; // known to be safe } + String node = computer.getName(); if (c.isAnnotationPresent(MasterToSlave.class)) { - throw new SecurityException(String.format("Invocation of %s is prohibited", c)); + throw new SecurityException("Sending " + name + " from " + node + " to master is prohibited"); } else { - // no annotation provided, so we don't know. - // to err on the correctness we'd let it pass with reporting, which - // provides auditing trail. - LOGGER.log(Level.WARNING, "Unchecked callable from {0}: {1}", new Object[] {computer.getName(), c}); - return stem; + // No annotation provided, so we do not know whether it is safe or not. + if (Boolean.getBoolean(BYPASS_PROP)) { + LOGGER.log(Level.FINE, "Allowing {0} to be sent from {1} to master", new Object[] {name, node}); + return stem; + } else if (Boolean.getBoolean(BYPASS_PROP + "." + name)) { + LOGGER.log(Level.FINE, "Explicitly allowing {0} to be sent from {1} to master", new Object[] {name, node}); + return stem; + } else { + throw new SecurityException("Sending from " + node + " to master is prohibited unless you run with: -D" + BYPASS_PROP + "." + name); + } } } From 4fabe923688758921f4eaaa9162c643e0e92c7ae Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Sat, 20 Sep 2014 15:44:12 -0400 Subject: [PATCH 23/54] One-letter typo caused massive test failures, obscuring any real problems. --- core/src/main/java/hudson/FilePath.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index f1d9ab637b..40450cb6dd 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -530,7 +530,7 @@ public final class FilePath implements Serializable { } else { File p = f.getParentFile(); if (p != null) { - mkdirs(f); + mkdirs(p); } IOUtils.copy(zip.getInputStream(e), writing(f)); try { From 308cf242272d8596f3eb64a48a9bccd7a3fc1aa8 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 22 Sep 2014 09:20:37 -0400 Subject: [PATCH 24/54] Adding logging to CallableDirectionChecker. Also clarified that this is impl, not API. --- .../jenkins/security/CallableDirectionChecker.java | 12 +++++++++--- .../main/java/jenkins/security/MasterToSlave.java | 1 - .../main/java/jenkins/security/SlaveToMaster.java | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index cfedb261fe..afc0eede21 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -9,20 +9,23 @@ import hudson.slaves.SlaveComputer; import java.util.logging.Level; import java.util.logging.Logger; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.DoNotUse; +import org.kohsuke.accmod.restrictions.NoExternalUse; /** - * Rejects {@link MasterToSlave} callables. + * Rejects non-{@link SlaveToMaster} callables. * * @author Kohsuke Kawaguchi - * @since TODO */ +@Restricted(NoExternalUse.class) // used implicitly via listener public class CallableDirectionChecker extends CallableDecorator { private static final String BYPASS_PROP = "jenkins.security.CallableDirectionChecker.allowUnmarkedCallables"; private final SlaveComputer computer; - public CallableDirectionChecker(SlaveComputer computer) { + private CallableDirectionChecker(SlaveComputer computer) { this.computer = computer; } @@ -32,10 +35,12 @@ public class CallableDirectionChecker extends CallableDecorator { String name = c.getName(); if (name.startsWith("hudson.remoting")) { // TODO probably insecure + LOGGER.log(Level.FINE, "Sending {0} to master is allowed since it is in remoting", name); return stem; // lower level services provided by remoting, such IOSyncer, RPCRequest, Ping, etc. that we allow } if (c.isAnnotationPresent(SlaveToMaster.class)) { + LOGGER.log(Level.FINE, "Sending {0} is allowed since it is marked @SlaveToMaster", name); return stem; // known to be safe } @@ -59,6 +64,7 @@ public class CallableDirectionChecker extends CallableDecorator { /** * Installs {@link CallableDirectionChecker} to every channel. */ + @Restricted(DoNotUse.class) // impl @Extension public static class ComputerListenerImpl extends ComputerListener { @Override diff --git a/core/src/main/java/jenkins/security/MasterToSlave.java b/core/src/main/java/jenkins/security/MasterToSlave.java index c5b03b6c70..832af307a3 100644 --- a/core/src/main/java/jenkins/security/MasterToSlave.java +++ b/core/src/main/java/jenkins/security/MasterToSlave.java @@ -15,7 +15,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; * * @author Kohsuke Kawaguchi * @see SlaveToMaster - * @see CallableDirectionChecker * @since 1.THU */ @Retention(RUNTIME) diff --git a/core/src/main/java/jenkins/security/SlaveToMaster.java b/core/src/main/java/jenkins/security/SlaveToMaster.java index 9422ad433f..d5ececbd22 100644 --- a/core/src/main/java/jenkins/security/SlaveToMaster.java +++ b/core/src/main/java/jenkins/security/SlaveToMaster.java @@ -15,6 +15,7 @@ import static java.lang.annotation.RetentionPolicy.*; * * @author Kohsuke Kawaguchi * @see MasterToSlave + * @see CallableDirectionChecker * @since 1.THU */ @Retention(RUNTIME) From 1869eac536d054113fbe2e10810c2cde1245ef43 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 22 Sep 2014 09:49:03 -0400 Subject: [PATCH 25/54] No real reason to mention node name in exception. --- .../security/CallableDirectionChecker.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index afc0eede21..d5b0a6d3c7 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -23,11 +23,7 @@ public class CallableDirectionChecker extends CallableDecorator { private static final String BYPASS_PROP = "jenkins.security.CallableDirectionChecker.allowUnmarkedCallables"; - private final SlaveComputer computer; - - private CallableDirectionChecker(SlaveComputer computer) { - this.computer = computer; - } + private CallableDirectionChecker() {} @Override public Callable userRequest(Callable op, Callable stem) { @@ -44,19 +40,18 @@ public class CallableDirectionChecker extends CallableDecorator { return stem; // known to be safe } - String node = computer.getName(); if (c.isAnnotationPresent(MasterToSlave.class)) { - throw new SecurityException("Sending " + name + " from " + node + " to master is prohibited"); + throw new SecurityException("Sending " + name + " from slave to master is prohibited"); } else { // No annotation provided, so we do not know whether it is safe or not. if (Boolean.getBoolean(BYPASS_PROP)) { - LOGGER.log(Level.FINE, "Allowing {0} to be sent from {1} to master", new Object[] {name, node}); + LOGGER.log(Level.FINE, "Allowing {0} to be sent from slave to master", name); return stem; } else if (Boolean.getBoolean(BYPASS_PROP + "." + name)) { - LOGGER.log(Level.FINE, "Explicitly allowing {0} to be sent from {1} to master", new Object[] {name, node}); + LOGGER.log(Level.FINE, "Explicitly allowing {0} to be sent from slave to master", name); return stem; } else { - throw new SecurityException("Sending from " + node + " to master is prohibited unless you run with: -D" + BYPASS_PROP + "." + name); + throw new SecurityException("Sending from slave to master is prohibited unless you run with: -D" + BYPASS_PROP + "." + name); } } } @@ -69,7 +64,7 @@ public class CallableDirectionChecker extends CallableDecorator { public static class ComputerListenerImpl extends ComputerListener { @Override public void onChannelBuilding(ChannelBuilder builder, SlaveComputer sc) { - builder.with(new CallableDirectionChecker(sc)); + builder.with(new CallableDirectionChecker()); } } From 755f55cbdf269604a0f9d5ae3569336734213d51 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 22 Sep 2014 10:58:48 -0400 Subject: [PATCH 26/54] installTo/uninstallFrom methods ought to be final. --- core/src/main/java/jenkins/FilePathFilter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/jenkins/FilePathFilter.java b/core/src/main/java/jenkins/FilePathFilter.java index 991a3a752d..47ba6d4182 100644 --- a/core/src/main/java/jenkins/FilePathFilter.java +++ b/core/src/main/java/jenkins/FilePathFilter.java @@ -58,7 +58,7 @@ public abstract class FilePathFilter { public void stat(File f) throws SecurityException {} - public void installTo(Channel ch) { + public final void installTo(Channel ch) { synchronized (ch) { FilePathFilterAggregator filters = ch.getProperty(FilePathFilterAggregator.KEY); if (filters==null) { @@ -69,7 +69,7 @@ public abstract class FilePathFilter { } } - public void uninstallFrom(Channel ch) { + public final void uninstallFrom(Channel ch) { synchronized (ch) { FilePathFilterAggregator filters = ch.getProperty(FilePathFilterAggregator.KEY); if (filters!=null) { From 735ba83148505ed08f665c03a3a871ec87ad1027 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 22 Sep 2014 11:50:55 -0400 Subject: [PATCH 27/54] Installing a default FilePathFilter that rejects everything. --- core/src/main/java/hudson/FilePath.java | 2 + .../security/DefaultFilePathFilter.java | 81 +++++++++++++++++++ .../security/DefaultFilePathFilterTest.java | 70 ++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 core/src/main/java/jenkins/security/DefaultFilePathFilter.java create mode 100644 test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 40450cb6dd..85ece8bf73 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -2446,6 +2446,8 @@ public final class FilePath implements Serializable { /** * Adapts {@link FileCallable} to {@link Callable}. */ + @MasterToSlave // the normal case + @SlaveToMaster // possible, but triggers FilePathFilter private class FileCallableWrapper implements DelegatingCallable { private final FileCallable callable; private transient ClassLoader classLoader; diff --git a/core/src/main/java/jenkins/security/DefaultFilePathFilter.java b/core/src/main/java/jenkins/security/DefaultFilePathFilter.java new file mode 100644 index 0000000000..89939553af --- /dev/null +++ b/core/src/main/java/jenkins/security/DefaultFilePathFilter.java @@ -0,0 +1,81 @@ +/* + * 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.security; + +import hudson.Extension; +import hudson.FilePath; +import hudson.model.Computer; +import hudson.model.TaskListener; +import hudson.remoting.Channel; +import hudson.slaves.ComputerListener; +import java.io.File; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; +import jenkins.FilePathFilter; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.DoNotUse; + +/** + * Blocks slaves from writing to files on the master by default. + */ +@Restricted(DoNotUse.class) // impl +@Extension public class DefaultFilePathFilter extends ComputerListener { + + static final String BYPASS_PROP = "jenkins.security.DefaultFilePathFilter.allow"; + private static final Logger LOGGER = Logger.getLogger(DefaultFilePathFilter.class.getName()); + + @Override public void preOnline(Computer c, Channel channel, FilePath root, TaskListener listener) throws IOException, InterruptedException { + new FilePathFilter() { + private void op(String op, File f) throws SecurityException { + if (Boolean.getBoolean(BYPASS_PROP)) { + LOGGER.log(Level.FINE, "slave allowed to {0} {1}", new Object[] {op, f}); + } else { + // TODO allow finer-grained control, for example by regexp (or Ant pattern) of relative path inside $JENKINS_HOME + throw new SecurityException("slave may not " + op + " " + f); + } + } + @Override public void read(File f) throws SecurityException { + op("read", f); + } + @Override public void write(File f) throws SecurityException { + op("write", f); + } + @Override public void mkdirs(File f) throws SecurityException { + op("mkdirs", f); + } + @Override public void create(File f) throws SecurityException { + op("create", f); + } + @Override public void delete(File f) throws SecurityException { + op("delete", f); + } + @Override public void stat(File f) throws SecurityException { + op("stat", f); + } + }.installTo(channel); + } + +} diff --git a/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java b/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java new file mode 100644 index 0000000000..d258879ab7 --- /dev/null +++ b/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java @@ -0,0 +1,70 @@ +/* + * 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.security; + +import hudson.FilePath; +import hudson.model.Slave; +import hudson.remoting.Callable; +import java.io.File; +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.Rule; +import org.jvnet.hudson.test.JenkinsRule; + +public class DefaultFilePathFilterTest { + + @Rule public JenkinsRule r = new JenkinsRule(); + + @Test public void remotePath() throws Exception { + Slave s = r.createOnlineSlave(); + s.getRootPath().child("forward").write("hello", null); + FilePath reverse = new FilePath(new File(r.jenkins.root, "reverse")); + assertFalse(reverse.exists()); + try { + s.getChannel().call(new CallableImpl(reverse)); + fail("should have failed"); + } catch (SecurityException x) { + // good + } + assertFalse(reverse.exists()); + System.setProperty(DefaultFilePathFilter.BYPASS_PROP, "true"); + s.getChannel().call(new CallableImpl(reverse)); + assertTrue(reverse.exists()); + assertEquals("goodbye", reverse.readToString()); + } + + private static class CallableImpl implements Callable { + private final FilePath p; + CallableImpl(FilePath p) { + this.p = p; + } + @Override public Void call() throws Exception { + assertTrue(p.isRemote()); + p.write("goodbye", null); + return null; + } + } + +} From 47f61e55da09ab6278c39f9c80719384ec2464b0 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 23 Sep 2014 15:56:45 -0400 Subject: [PATCH 28/54] Better toString(). --- core/src/main/java/jenkins/FilePathFilter.java | 3 +-- core/src/main/java/jenkins/FilePathFilterAggregator.java | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/jenkins/FilePathFilter.java b/core/src/main/java/jenkins/FilePathFilter.java index 47ba6d4182..b738a35f42 100644 --- a/core/src/main/java/jenkins/FilePathFilter.java +++ b/core/src/main/java/jenkins/FilePathFilter.java @@ -102,6 +102,5 @@ public abstract class FilePathFilter { return f; } - private static final FilePathFilter NOOP = new FilePathFilter() { - }; + private static final FilePathFilter NOOP = new FilePathFilterAggregator(); } diff --git a/core/src/main/java/jenkins/FilePathFilterAggregator.java b/core/src/main/java/jenkins/FilePathFilterAggregator.java index 2249b276b2..3796904ca2 100644 --- a/core/src/main/java/jenkins/FilePathFilterAggregator.java +++ b/core/src/main/java/jenkins/FilePathFilterAggregator.java @@ -60,5 +60,9 @@ class FilePathFilterAggregator extends FilePathFilter { } } + @Override public String toString() { + return "FilePathFilterAggregator" + all; + } + static final ChannelProperty KEY = new ChannelProperty(FilePathFilterAggregator.class, "FilePathFilters"); } From 14d33d4ba4c86fffd57a1a14555f3fb4084bb894 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 23 Sep 2014 16:05:10 -0400 Subject: [PATCH 29/54] Demonstrating that local FilePath operations within a slave callable are unaffected by FilePathFilter. This is because we are not installing a filter on the reverse channel. --- .../security/DefaultFilePathFilterTest.java | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java b/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java index d258879ab7..80c4cef801 100644 --- a/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java +++ b/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java @@ -39,25 +39,38 @@ public class DefaultFilePathFilterTest { @Test public void remotePath() throws Exception { Slave s = r.createOnlineSlave(); - s.getRootPath().child("forward").write("hello", null); + FilePath forward = s.getRootPath().child("forward"); + forward.write("hello", null); + assertEquals("hello", s.getRootPath().act(new LocalCallable(forward))); FilePath reverse = new FilePath(new File(r.jenkins.root, "reverse")); assertFalse(reverse.exists()); try { - s.getChannel().call(new CallableImpl(reverse)); + s.getChannel().call(new ReverseCallable(reverse)); fail("should have failed"); } catch (SecurityException x) { // good } assertFalse(reverse.exists()); System.setProperty(DefaultFilePathFilter.BYPASS_PROP, "true"); - s.getChannel().call(new CallableImpl(reverse)); + s.getChannel().call(new ReverseCallable(reverse)); assertTrue(reverse.exists()); assertEquals("goodbye", reverse.readToString()); } - private static class CallableImpl implements Callable { + private static class LocalCallable implements Callable { private final FilePath p; - CallableImpl(FilePath p) { + LocalCallable(FilePath p) { + this.p = p; + } + @Override public String call() throws Exception { + assertFalse(p.isRemote()); + return p.readToString(); + } + } + + private static class ReverseCallable implements Callable { + private final FilePath p; + ReverseCallable(FilePath p) { this.p = p; } @Override public Void call() throws Exception { From 2fe5b96f55467df631c3faa55082a04df3bc14ae Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 23 Sep 2014 16:26:27 -0400 Subject: [PATCH 30/54] For testing purposes, adding dry-run system properties allowing potential violations to be written to log files. --- .../security/CallableDirectionChecker.java | 22 +++++++++++++++++++ .../security/DefaultFilePathFilter.java | 21 ++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index d5b0a6d3c7..b20ec92057 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -6,6 +6,10 @@ import hudson.remoting.CallableDecorator; import hudson.remoting.ChannelBuilder; import hudson.slaves.ComputerListener; import hudson.slaves.SlaveComputer; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; import java.util.logging.Level; import java.util.logging.Logger; @@ -22,6 +26,19 @@ import org.kohsuke.accmod.restrictions.NoExternalUse; public class CallableDirectionChecker extends CallableDecorator { private static final String BYPASS_PROP = "jenkins.security.CallableDirectionChecker.allowUnmarkedCallables"; + private static final PrintWriter BYPASS_LOG; + static { + String log = System.getProperty("jenkins.security.CallableDirectionChecker.log"); + if (log == null) { + BYPASS_LOG = null; + } else { + try { + BYPASS_LOG = new PrintWriter(new OutputStreamWriter(new FileOutputStream(log, true)), true); + } catch (FileNotFoundException x) { + throw new ExceptionInInitializerError(x); + } + } + } private CallableDirectionChecker() {} @@ -40,6 +57,11 @@ public class CallableDirectionChecker extends CallableDecorator { return stem; // known to be safe } + if (BYPASS_LOG != null) { + BYPASS_LOG.println(name); + return stem; + } + if (c.isAnnotationPresent(MasterToSlave.class)) { throw new SecurityException("Sending " + name + " from slave to master is prohibited"); } else { diff --git a/core/src/main/java/jenkins/security/DefaultFilePathFilter.java b/core/src/main/java/jenkins/security/DefaultFilePathFilter.java index 89939553af..310bf775ff 100644 --- a/core/src/main/java/jenkins/security/DefaultFilePathFilter.java +++ b/core/src/main/java/jenkins/security/DefaultFilePathFilter.java @@ -31,7 +31,11 @@ import hudson.model.TaskListener; import hudson.remoting.Channel; import hudson.slaves.ComputerListener; import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; import java.util.logging.Level; import java.util.logging.Logger; import jenkins.FilePathFilter; @@ -45,11 +49,28 @@ import org.kohsuke.accmod.restrictions.DoNotUse; @Extension public class DefaultFilePathFilter extends ComputerListener { static final String BYPASS_PROP = "jenkins.security.DefaultFilePathFilter.allow"; + private static final PrintWriter BYPASS_LOG; + static { + String log = System.getProperty("jenkins.security.DefaultFilePathFilter.log"); + if (log == null) { + BYPASS_LOG = null; + } else { + try { + BYPASS_LOG = new PrintWriter(new OutputStreamWriter(new FileOutputStream(log, true)), true); + } catch (FileNotFoundException x) { + throw new ExceptionInInitializerError(x); + } + } + } private static final Logger LOGGER = Logger.getLogger(DefaultFilePathFilter.class.getName()); @Override public void preOnline(Computer c, Channel channel, FilePath root, TaskListener listener) throws IOException, InterruptedException { new FilePathFilter() { private void op(String op, File f) throws SecurityException { + if (BYPASS_LOG != null) { + BYPASS_LOG.println(op + " " + f); + return; + } if (Boolean.getBoolean(BYPASS_PROP)) { LOGGER.log(Level.FINE, "slave allowed to {0} {1}", new Object[] {op, f}); } else { From c0260056e5098a21bfacc939ed187bef21abbd93 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Tue, 23 Sep 2014 13:49:05 -0700 Subject: [PATCH 31/54] in pre-cursor for the next change, make all remoting call go through FileCallable --- core/src/main/java/hudson/FilePath.java | 26 ++++++++++--------- .../main/java/hudson/tools/JDKInstaller.java | 4 +-- .../main/java/jenkins/util/VirtualFile.java | 6 ++++- ...eopenableRotatingFileOutputStreamTest.java | 2 +- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 85ece8bf73..e78904be7e 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -453,7 +453,7 @@ public final class FilePath implements Serializable { public void unzip(final FilePath target) throws IOException, InterruptedException { target.act(new FileCallable() { - public Void invoke(File dir, VirtualChannel channel) throws IOException { + public Void invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException { if (FilePath.this.isRemote()) unzip(dir, FilePath.this.read()); // use streams else @@ -476,7 +476,7 @@ public final class FilePath implements Serializable { */ public void untar(final FilePath target, final TarCompression compression) throws IOException, InterruptedException { target.act(new FileCallable() { - public Void invoke(File dir, VirtualChannel channel) throws IOException { + public Void invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException { readFromTar(FilePath.this.getName(),dir,compression.extract(FilePath.this.read())); return null; } @@ -1637,18 +1637,20 @@ public final class FilePath implements Serializable { /** * Reads this file. */ - public InputStream read() throws IOException { + public InputStream read() throws IOException, InterruptedException { if(channel==null) return new FileInputStream(reading(new File(remote))); final Pipe p = Pipe.createRemoteToLocal(); - channel.callAsync(new Callable() { + actAsync(new FileCallable() { private static final long serialVersionUID = 1L; - public Void call() throws IOException { - FileInputStream fis=null; + + @Override + public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { + FileInputStream fis = null; try { - fis = new FileInputStream(reading(new File(remote))); - Util.copyStream(fis,p.getOut()); + fis = new FileInputStream(reading(f)); + Util.copyStream(fis, p.getOut()); return null; } finally { IOUtils.closeQuietly(fis); @@ -1663,7 +1665,7 @@ public final class FilePath implements Serializable { /** * Reads this file into a string, by using the current system encoding. */ - public String readToString() throws IOException { + public String readToString() throws IOException, InterruptedException { InputStream in = read(); try { return IOUtils.toString(in); @@ -1694,10 +1696,10 @@ public final class FilePath implements Serializable { return new FileOutputStream(writing(f)); } - return channel.call(new Callable() { + return act(new FileCallable() { private static final long serialVersionUID = 1L; - public OutputStream call() throws IOException { - File f = new File(remote).getAbsoluteFile(); + public OutputStream invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { + f = f.getAbsoluteFile(); mkdirs(f.getParentFile()); FileOutputStream fos = new FileOutputStream(writing(f)); return new RemoteOutputStream(fos); diff --git a/core/src/main/java/hudson/tools/JDKInstaller.java b/core/src/main/java/hudson/tools/JDKInstaller.java index 2b6fc24327..5bb4e377ef 100644 --- a/core/src/main/java/hudson/tools/JDKInstaller.java +++ b/core/src/main/java/hudson/tools/JDKInstaller.java @@ -270,7 +270,7 @@ public class JDKInstaller extends ToolInstaller { public interface FileSystem { void delete(String file) throws IOException, InterruptedException; void chmod(String file,int mode) throws IOException, InterruptedException; - InputStream read(String file) throws IOException; + InputStream read(String file) throws IOException, InterruptedException; /** * List sub-directories of the given directory and just return the file name portion. */ @@ -293,7 +293,7 @@ public class JDKInstaller extends ToolInstaller { $(file).chmod(mode); } - public InputStream read(String file) throws IOException { + public InputStream read(String file) throws IOException, InterruptedException { return $(file).read(); } diff --git a/core/src/main/java/jenkins/util/VirtualFile.java b/core/src/main/java/jenkins/util/VirtualFile.java index e0180f6ded..5c985ac9c9 100644 --- a/core/src/main/java/jenkins/util/VirtualFile.java +++ b/core/src/main/java/jenkins/util/VirtualFile.java @@ -326,7 +326,11 @@ public abstract class VirtualFile implements Comparable { } } @Override public InputStream open() throws IOException { - return f.read(); + try { + return f.read(); + } catch (InterruptedException x) { + throw (IOException) new IOException(x.toString()).initCause(x); + } } }; } diff --git a/core/src/test/java/hudson/util/io/ReopenableRotatingFileOutputStreamTest.java b/core/src/test/java/hudson/util/io/ReopenableRotatingFileOutputStreamTest.java index 4750cab10c..9129d06888 100644 --- a/core/src/test/java/hudson/util/io/ReopenableRotatingFileOutputStreamTest.java +++ b/core/src/test/java/hudson/util/io/ReopenableRotatingFileOutputStreamTest.java @@ -11,7 +11,7 @@ import java.io.PrintWriter; * @author Kohsuke Kawaguchi */ public class ReopenableRotatingFileOutputStreamTest extends TestCase { - public void testRotation() throws IOException { + public void testRotation() throws IOException, InterruptedException { File base = File.createTempFile("test", "log"); ReopenableRotatingFileOutputStream os = new ReopenableRotatingFileOutputStream(base,3); PrintWriter w = new PrintWriter(os,true); From 140d929516a2b47401de173d72196c5214a43d9b Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Tue, 23 Sep 2014 14:42:39 -0700 Subject: [PATCH 32/54] Fixed the computation of effective FilePathFilter. If a @SlaveToMaster Callable gets sent to the master, and if inside its call() method the code directly/indirectly performs file system access (for example on files and directories that it didn't receive as parameters), that is not meant to be the subject of the filter check. Rather, the check is meant for FilePath that the other side sends to us, when it points to a local file. So it makes more sense to compute the effective FilePathFilter during the deserialization, as opposed to doing so when the actual operation is attempted. Various FileCallable impls in FilePath fits this pattern, as it drags outer FilePath instance with itself. In addition, this change addresses other situations correctly, where FilePath is used as arguments and return values of other Callables, such as the following: FilePath f = channel.call(new SlaveWillDoSomeComputationAndGiveMeTheResultAsFile()); f.copyTo(build.getRootDir().child("data.xml")) "f.copyTo" happens in the context of Channel.current()==null, but we need to check that 'f' is legit and not something like new FilePath(new File("/etc/shadow")) --- core/src/main/java/hudson/FilePath.java | 60 +++++++++++++------ .../src/main/java/jenkins/FilePathFilter.java | 23 ++++--- 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index e78904be7e..60f7b9ac4f 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -70,6 +70,8 @@ import org.apache.tools.zip.ZipEntry; import org.apache.tools.zip.ZipFile; import org.kohsuke.stapler.Stapler; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileFilter; @@ -194,6 +196,17 @@ public final class FilePath implements Serializable { // since the platform of the slave might be different, can't use java.io.File private final String remote; + /** + * If this {@link FilePath} is deserialized to handle file access request from a remote computer, + * this field is set to the filter that performs access control. + * + *

    + * If null, no access control is needed. + * + * @see #filterNonNull() + */ + private transient @Nullable FilePathFilter filter; + /** * Creates a {@link FilePath} that represents a path on the given node. * @@ -503,7 +516,7 @@ public final class FilePath implements Serializable { }); } - private static void unzip(File dir, InputStream in) throws IOException { + private void unzip(File dir, InputStream in) throws IOException { File tmpFile = File.createTempFile("tmpzip", null); // uses java.io.tmpdir try { // TODO why does this not simply use ZipInputStream? @@ -515,7 +528,7 @@ public final class FilePath implements Serializable { } } - static private void unzip(File dir, File zipFile) throws IOException { + private void unzip(File dir, File zipFile) throws IOException { dir = dir.getAbsoluteFile(); // without absolutization, getParentFile below seems to fail ZipFile zip = new ZipFile(zipFile); @SuppressWarnings("unchecked") @@ -774,7 +787,7 @@ public final class FilePath implements Serializable { } @MasterToSlave // this reads from arbitrary URL - private static final class Unpack implements FileCallable { + private final class Unpack implements FileCallable { private final URL archive; Unpack(URL archive) { this.archive = archive; @@ -2050,7 +2063,7 @@ public final class FilePath implements Serializable { * @return * number of files/directories that are written. */ - private static Integer writeToTar(File baseDir, DirScanner scanner, OutputStream out) throws IOException { + private Integer writeToTar(File baseDir, DirScanner scanner, OutputStream out) throws IOException { Archiver tw = ArchiverFactory.TAR.create(out); try { scanner.scan(baseDir,reading(tw)); @@ -2063,7 +2076,7 @@ public final class FilePath implements Serializable { /** * Reads from a tar stream and stores obtained files to the base dir. */ - private static void readFromTar(String name, File baseDir, InputStream in) throws IOException { + private void readFromTar(String name, File baseDir, InputStream in) throws IOException { TarInputStream t = new TarInputStream(in); try { TarEntry te; @@ -2434,8 +2447,14 @@ public final class FilePath implements Serializable { ois.defaultReadObject(); if(ois.readBoolean()) { this.channel = channel; + this.filter = null; } else { this.channel = null; + // If the remote channel wants us to create a FilePath that points to a local file, + // we need to make sure the access control takes place. + // This covers the immediate case of FileCallables taking FilePath into reference closure implicitly, + // but it also covers more general case of FilePath sent as a return value or argument. + this.filter = FilePathFilter.current(); } } @@ -2548,13 +2567,16 @@ public final class FilePath implements Serializable { scanSingle(new File(dir, workspacePath), archivedPath, visitor); } } + } + private @Nonnull FilePathFilter filterNonNull() { + return filter!=null ? filter : FilePathFilter.EMPTY; } /** * Wraps {@link FileVisitor} to notify read access to {@link FilePathFilter}. */ - private static FileVisitor reading(final FileVisitor v) { + private FileVisitor reading(final FileVisitor v) { final FilePathFilter filter = FilePathFilter.current(); if (filter==null) return v; @@ -2581,32 +2603,32 @@ public final class FilePath implements Serializable { /** * Pass through 'f' after ensuring that we can read that file. */ - private static File reading(File f) { - FilePathFilter.currentNonnull().read(f); + private File reading(File f) { + filterNonNull().read(f); return f; } /** * Pass through 'f' after ensuring that we can access the file attributes. */ - private static File stating(File f) { - FilePathFilter.currentNonnull().stat(f); + private File stating(File f) { + filterNonNull().stat(f); return f; } /** * Pass through 'f' after ensuring that we can create that file/dir. */ - private static File creating(File f) { - FilePathFilter.currentNonnull().create(f); + private File creating(File f) { + filterNonNull().create(f); return f; } /** * Pass through 'f' after ensuring that we can write to that file. */ - private static File writing(File f) { - FilePathFilter filter = FilePathFilter.currentNonnull(); + private File writing(File f) { + FilePathFilter filter = filterNonNull(); if (!f.exists()) filter.create(f); filter.write(f); @@ -2614,18 +2636,18 @@ public final class FilePath implements Serializable { } /** - * Pass through 'f' after ensuring that we can write to that file. + * Pass through 'f' after ensuring that we can delete that file. */ - private static File deleting(File f) { - FilePathFilter.currentNonnull().delete(f); + private File deleting(File f) { + filterNonNull().delete(f); return f; } - private static boolean mkdirs(File dir) { + private boolean mkdirs(File dir) { if (dir.exists()) return false; - FilePathFilter.currentNonnull().mkdirs(dir); + filterNonNull().mkdirs(dir); return dir.mkdirs(); } } diff --git a/core/src/main/java/jenkins/FilePathFilter.java b/core/src/main/java/jenkins/FilePathFilter.java index b738a35f42..1c98933cc5 100644 --- a/core/src/main/java/jenkins/FilePathFilter.java +++ b/core/src/main/java/jenkins/FilePathFilter.java @@ -4,7 +4,6 @@ import hudson.FilePath; import hudson.remoting.Channel; import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; import java.io.File; /** @@ -90,17 +89,17 @@ public abstract class FilePathFilter { } /** - * A flavor of {@link #current()} that returns a place holder empty object if none is needed. - * - * @return - * never null. If no filter is in scope, this method returns a non-null object that - * does nothing. + * Immutable instance that represents the empty filter. */ - public static @Nonnull FilePathFilter currentNonnull() { - FilePathFilter f = current(); - if (f==null) return NOOP; - return f; - } + public static final FilePathFilter EMPTY = new FilePathFilterAggregator() { + @Override + public void add(FilePathFilter f) { + // noop + } - private static final FilePathFilter NOOP = new FilePathFilterAggregator(); + @Override + public String toString() { + return "None"; + } + }; } From 1295aa9180ee9e24ba8609410120d5da4cde5fa2 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Tue, 23 Sep 2014 14:49:31 -0700 Subject: [PATCH 33/54] I prefer strongly typed named variable for controlling feature flags. It's faster, it's typed, and failing to use the correct name results in an error, so you know when the flag gets removed. Making it more friendly for Groovy script. --- .../java/jenkins/security/CallableDirectionChecker.java | 6 ++++-- .../java/jenkins/security/DefaultFilePathFilter.java | 9 +++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index b20ec92057..e947ce84c4 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -21,11 +21,13 @@ import org.kohsuke.accmod.restrictions.NoExternalUse; * Rejects non-{@link SlaveToMaster} callables. * * @author Kohsuke Kawaguchi + * @since 1.THU */ @Restricted(NoExternalUse.class) // used implicitly via listener public class CallableDirectionChecker extends CallableDecorator { - private static final String BYPASS_PROP = "jenkins.security.CallableDirectionChecker.allowUnmarkedCallables"; + private static final String BYPASS_PROP = CallableDirectionChecker.class.getName()+".allow"; + public static boolean BYPASS = Boolean.getBoolean(BYPASS_PROP); private static final PrintWriter BYPASS_LOG; static { String log = System.getProperty("jenkins.security.CallableDirectionChecker.log"); @@ -66,7 +68,7 @@ public class CallableDirectionChecker extends CallableDecorator { throw new SecurityException("Sending " + name + " from slave to master is prohibited"); } else { // No annotation provided, so we do not know whether it is safe or not. - if (Boolean.getBoolean(BYPASS_PROP)) { + if (BYPASS) { LOGGER.log(Level.FINE, "Allowing {0} to be sent from slave to master", name); return stem; } else if (Boolean.getBoolean(BYPASS_PROP + "." + name)) { diff --git a/core/src/main/java/jenkins/security/DefaultFilePathFilter.java b/core/src/main/java/jenkins/security/DefaultFilePathFilter.java index 310bf775ff..d4032cad61 100644 --- a/core/src/main/java/jenkins/security/DefaultFilePathFilter.java +++ b/core/src/main/java/jenkins/security/DefaultFilePathFilter.java @@ -44,11 +44,16 @@ import org.kohsuke.accmod.restrictions.DoNotUse; /** * Blocks slaves from writing to files on the master by default. + * + * @since 1.THU */ @Restricted(DoNotUse.class) // impl @Extension public class DefaultFilePathFilter extends ComputerListener { - static final String BYPASS_PROP = "jenkins.security.DefaultFilePathFilter.allow"; + /** + * Escape hatch to disable this check completely. + */ + public static boolean BYPASS = Boolean.getBoolean(DefaultFilePathFilter.class.getName()+".allow"); private static final PrintWriter BYPASS_LOG; static { String log = System.getProperty("jenkins.security.DefaultFilePathFilter.log"); @@ -71,7 +76,7 @@ import org.kohsuke.accmod.restrictions.DoNotUse; BYPASS_LOG.println(op + " " + f); return; } - if (Boolean.getBoolean(BYPASS_PROP)) { + if (BYPASS) { LOGGER.log(Level.FINE, "slave allowed to {0} {1}", new Object[] {op, f}); } else { // TODO allow finer-grained control, for example by regexp (or Ant pattern) of relative path inside $JENKINS_HOME From 59456c734addd224e43a546d2c57885ce2ee74aa Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Tue, 23 Sep 2014 18:00:04 -0700 Subject: [PATCH 34/54] Defined three remoting roles in Jenkins. (This requires 82211d10bfc7d066121ba80c8a6755d1dae97bf2 in remoting) --- .../src/main/java/jenkins/security/Roles.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 core/src/main/java/jenkins/security/Roles.java diff --git a/core/src/main/java/jenkins/security/Roles.java b/core/src/main/java/jenkins/security/Roles.java new file mode 100644 index 0000000000..e2a65d6efd --- /dev/null +++ b/core/src/main/java/jenkins/security/Roles.java @@ -0,0 +1,38 @@ +package jenkins.security; + +import org.jenkinsci.remoting.Role; + +import java.util.Collection; +import java.util.Collections; + +/** + * Predefined {@link Role}s in Jenkins. + * + *

    + * In Jenkins, there is really only one interesting role, which is the Jenkins master. + * Slaves, CLI, and Maven processes are all going to load classes from the master, + * which means it accepts anything that the master asks for, and thus they need + * not have any role. + * + * @author Kohsuke Kawaguchi + */ +public class Roles { + public static final Role MASTER = new Role("master"); + + /** + * Indicates that a callable runs on masters, requested by slaves/CLI/maven/whatever. + */ + public static final Collection FOR_MASTER = Collections.singleton(MASTER); + + public static final Role SLAVE = new Role("slave"); + + /** + * Indicates that a callable is meant to run on slaves. + * + * This isn't used to reject callables to run on the slave, but rather to allow + * the master to promptly reject callables that are really not meant to be run on + * the master (as opposed to ones that do not have that information, which gets + * {@link Role#UNKNOWN}) + */ + public static final Collection FOR_SLAVE = Collections.singleton(SLAVE); +} From ffb5b65093bb2afd5832cac4ea6799409abc05c4 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Tue, 23 Sep 2014 18:33:56 -0700 Subject: [PATCH 35/54] Switched from annotation based scheme to a function based scheme. --- core/src/main/java/hudson/EnvVars.java | 5 +- core/src/main/java/hudson/FilePath.java | 144 ++++++++++++------ core/src/main/java/hudson/Launcher.java | 11 +- core/src/main/java/hudson/cli/CLICommand.java | 12 +- .../java/hudson/cli/CommandDuringBuild.java | 5 +- .../java/hudson/cli/InstallToolCommand.java | 8 +- .../java/hudson/cli/util/ScriptLoader.java | 6 +- .../main/java/hudson/logging/LogRecorder.java | 5 +- .../main/java/hudson/model/AbstractItem.java | 4 +- core/src/main/java/hudson/model/Computer.java | 11 +- core/src/main/java/hudson/model/Slave.java | 9 +- .../node_monitors/ArchitectureMonitor.java | 5 +- .../DiskSpaceMonitorDescriptor.java | 5 +- .../node_monitors/ResponseTimeMonitor.java | 10 +- .../node_monitors/SwapSpaceMonitor.java | 6 +- .../node_monitors/TemporarySpaceMonitor.java | 4 +- .../hudson/os/solaris/ZFSProvisioner.java | 8 +- .../AbstractPasswordBasedSecurityRealm.java | 6 +- .../java/hudson/slaves/ChannelPinger.java | 6 +- .../slaves/ConnectionActivityMonitor.java | 7 +- .../java/hudson/slaves/SlaveComputer.java | 27 ++-- .../java/hudson/tasks/ArtifactArchiver.java | 3 +- .../main/java/hudson/tasks/Fingerprinter.java | 3 +- core/src/main/java/hudson/tasks/Maven.java | 3 +- core/src/main/java/hudson/tasks/Shell.java | 6 +- .../java/hudson/tasks/junit/JUnitParser.java | 5 +- .../test/DefaultTestResultParserImpl.java | 4 +- .../main/java/hudson/tools/JDKInstaller.java | 8 +- .../hudson/tools/ZipExtractionInstaller.java | 4 +- .../java/hudson/util/RemotingDiagnostics.java | 14 +- .../java/jenkins/security/MasterToSlave.java | 25 --- .../security/MasterToSlaveCallable.java | 11 +- .../NotReallyRoleSensitiveCallable.java | 21 +++ .../java/jenkins/security/SlaveToMaster.java | 26 ---- .../security/SlaveToMasterCallable.java | 11 +- .../jenkins/slaves/StandardOutputSwapper.java | 6 +- .../main/java/jenkins/util/VirtualFile.java | 6 +- core/src/test/java/hudson/LauncherTest.java | 4 +- core/src/test/java/hudson/os/SUTester.java | 3 +- .../java/hudson/util/ProcessTreeTest.java | 3 +- test/src/test/java/hudson/ProcTest.java | 5 +- .../logging/LogRecorderManagerTest.java | 3 +- .../java/hudson/slaves/JNLPLauncherTest.java | 6 +- .../security/DefaultFilePathFilterTest.java | 14 +- 44 files changed, 247 insertions(+), 251 deletions(-) delete mode 100644 core/src/main/java/jenkins/security/MasterToSlave.java create mode 100644 core/src/main/java/jenkins/security/NotReallyRoleSensitiveCallable.java delete mode 100644 core/src/main/java/jenkins/security/SlaveToMaster.java diff --git a/core/src/main/java/hudson/EnvVars.java b/core/src/main/java/hudson/EnvVars.java index b0a9962259..92d918077b 100644 --- a/core/src/main/java/hudson/EnvVars.java +++ b/core/src/main/java/hudson/EnvVars.java @@ -26,7 +26,7 @@ package hudson; import hudson.remoting.Callable; import hudson.remoting.VirtualChannel; import hudson.util.CaseInsensitiveComparator; -import jenkins.security.MasterToSlave; +import jenkins.security.MasterToSlaveCallable; import java.io.File; import java.io.IOException; @@ -213,8 +213,7 @@ public class EnvVars extends TreeMap { return channel.call(new GetEnvVars()); } - @MasterToSlave - private static final class GetEnvVars implements Callable { + private static final class GetEnvVars extends MasterToSlaveCallable { public EnvVars call() { return new EnvVars(EnvVars.masterEnvVars); } diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 60f7b9ac4f..ed911a4a0a 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -55,9 +55,8 @@ import hudson.util.io.Archiver; import hudson.util.io.ArchiverFactory; import jenkins.FilePathFilter; import jenkins.model.Jenkins; -import jenkins.security.MasterToSlave; import jenkins.security.MasterToSlaveCallable; -import jenkins.security.SlaveToMaster; +import jenkins.security.Roles; import jenkins.util.VirtualFile; import org.apache.commons.fileupload.FileItem; import org.apache.commons.io.input.CountingInputStream; @@ -68,6 +67,8 @@ import org.apache.tools.ant.types.FileSet; import org.apache.tools.tar.TarEntry; import org.apache.tools.zip.ZipEntry; import org.apache.tools.zip.ZipFile; +import org.jenkinsci.remoting.Role; +import org.jenkinsci.remoting.RoleSensitive; import org.kohsuke.stapler.Stapler; import javax.annotation.Nonnull; @@ -94,6 +95,8 @@ import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.List; @@ -432,7 +435,7 @@ public final class FilePath implements Serializable { */ public int archive(final ArchiverFactory factory, OutputStream os, final DirScanner scanner) throws IOException, InterruptedException { final OutputStream out = (channel!=null)?new RemoteOutputStream(os):os; - return act(new FileCallable() { + return act(new SecureFileCallable() { public Integer invoke(File f, VirtualChannel channel) throws IOException { Archiver a = factory.create(out); try { @@ -464,7 +467,7 @@ public final class FilePath implements Serializable { * @see #unzipFrom(InputStream) */ public void unzip(final FilePath target) throws IOException, InterruptedException { - target.act(new FileCallable() { + target.act(new SecureFileCallable() { public Void invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException { if (FilePath.this.isRemote()) @@ -488,7 +491,7 @@ public final class FilePath implements Serializable { * @see #untarFrom(InputStream, TarCompression) */ public void untar(final FilePath target, final TarCompression compression) throws IOException, InterruptedException { - target.act(new FileCallable() { + target.act(new SecureFileCallable() { public Void invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException { readFromTar(FilePath.this.getName(),dir,compression.extract(FilePath.this.read())); return null; @@ -507,7 +510,7 @@ public final class FilePath implements Serializable { */ public void unzipFrom(InputStream _in) throws IOException, InterruptedException { final InputStream in = new RemoteInputStream(_in); - act(new FileCallable() { + act(new SecureFileCallable() { public Void invoke(File dir, VirtualChannel channel) throws IOException { unzip(dir, in); return null; @@ -566,7 +569,7 @@ public final class FilePath implements Serializable { * Absolutizes this {@link FilePath} and returns the new one. */ public FilePath absolutize() throws IOException, InterruptedException { - return new FilePath(channel,act(new FileCallable() { + return new FilePath(channel,act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public String invoke(File f, VirtualChannel channel) throws IOException { return f.getAbsolutePath(); @@ -584,7 +587,7 @@ public final class FilePath implements Serializable { * @since 1.456 */ public void symlinkTo(final String target, final TaskListener listener) throws IOException, InterruptedException { - act(new FileCallable() { + act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { writing(f); @@ -602,7 +605,7 @@ public final class FilePath implements Serializable { * @since 1.456 */ public String readLink() throws IOException, InterruptedException { - return act(new FileCallable() { + return act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public String invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { return Util.resolveSymlink(reading(f)); @@ -676,7 +679,7 @@ public final class FilePath implements Serializable { public void untarFrom(InputStream _in, final TarCompression compression) throws IOException, InterruptedException { try { final InputStream in = new RemoteInputStream(_in); - act(new FileCallable() { + act(new SecureFileCallable() { public Void invoke(File dir, VirtualChannel channel) throws IOException { readFromTar("input stream",dir, compression.extract(in)); return null; @@ -786,8 +789,8 @@ public final class FilePath implements Serializable { } } - @MasterToSlave // this reads from arbitrary URL - private final class Unpack implements FileCallable { + // this reads from arbitrary URL + private final class Unpack extends MasterToSlaveFileCallable { private final URL archive; Unpack(URL archive) { this.archive = archive; @@ -881,9 +884,14 @@ 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. + * + *

    + * Subtypes would likely want to extend from either {@link MasterToSlaveCallable} + * or {@link SlaveToMasterFileCallable}. + * * @see FilePath#act(FileCallable) */ - public interface FileCallable extends Serializable { + public interface FileCallable extends Serializable, RoleSensitive { /** * Performs the computational task on the node where the data is located. * @@ -899,6 +907,37 @@ public final class FilePath implements Serializable { T invoke(File f, VirtualChannel channel) throws IOException, InterruptedException; } + /** + * {@link FileCallable}s that are meant to be only used on the master. + */ + public static abstract class MasterToSlaveFileCallable implements FileCallable { + @Override + public Collection getRecipients() { + return Roles.FOR_SLAVE; + } + private static final long serialVersionUID = 1L; + } + + /** + * {@link FileCallable}s that can be executed on the master, sent by the slave. + */ + public static abstract class SlaveToMasterFileCallable implements FileCallable { + @Override + public Collection getRecipients() { + return Roles.FOR_MASTER; + } + private static final long serialVersionUID = 1L; + } + + /** + * {@link FileCallable}s that can be executed anywhere, including the master. + * + * The code is the same as {@link SlaveToMasterFileCallable}, but used as a marker to + * designate those impls that use {@link FilePathFilter}. + */ + /*package*/ static abstract class SecureFileCallable extends SlaveToMasterFileCallable { + } + /** * Executes some program on the machine that this {@link FilePath} exists, * so that one can perform local file operations. @@ -1044,6 +1083,12 @@ public final class FilePath implements Serializable { throw (IOException)new InterruptedIOException().initCause(e); } } + + @Override + public Collection getRecipients() { + return task.getRecipients(); + } + private static final long serialVersionUID = 1L; }; } @@ -1053,7 +1098,7 @@ public final class FilePath implements Serializable { * on which this file is available. */ public URI toURI() throws IOException, InterruptedException { - return act(new FileCallable() { + return act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public URI invoke(File f, VirtualChannel channel) { return f.toURI(); @@ -1074,7 +1119,7 @@ public final class FilePath implements Serializable { * Creates this directory. */ public void mkdirs() throws IOException, InterruptedException { - if(!act(new FileCallable() { + if(!act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public Boolean invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { if(mkdirs(f) || f.exists()) @@ -1093,7 +1138,7 @@ public final class FilePath implements Serializable { * Deletes this directory, including all its contents recursively. */ public void deleteRecursive() throws IOException, InterruptedException { - act(new FileCallable() { + act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public Void invoke(File f, VirtualChannel channel) throws IOException { deleteRecursive(deleting(f)); @@ -1106,7 +1151,7 @@ public final class FilePath implements Serializable { * Deletes all the contents of this directory, but not the directory itself */ public void deleteContents() throws IOException, InterruptedException { - act(new FileCallable() { + act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public Void invoke(File f, VirtualChannel channel) throws IOException { deleteContentsRecursive(f); @@ -1223,7 +1268,7 @@ public final class FilePath implements Serializable { */ public FilePath createTempFile(final String prefix, final String suffix) throws IOException, InterruptedException { try { - return new FilePath(this,act(new FileCallable() { + return new FilePath(this,act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public String invoke(File dir, VirtualChannel channel) throws IOException { File f = writing(File.createTempFile(prefix, suffix, dir)); @@ -1279,7 +1324,7 @@ public final class FilePath implements Serializable { */ public FilePath createTextTempFile(final String prefix, final String suffix, final String contents, final boolean inThisDirectory) throws IOException, InterruptedException { try { - return new FilePath(channel,act(new FileCallable() { + return new FilePath(channel,act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public String invoke(File dir, VirtualChannel channel) throws IOException { if(!inThisDirectory) @@ -1325,7 +1370,7 @@ public final class FilePath implements Serializable { */ public FilePath createTempDir(final String prefix, final String suffix) throws IOException, InterruptedException { try { - return new FilePath(this,act(new FileCallable() { + return new FilePath(this,act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public String invoke(File dir, VirtualChannel channel) throws IOException { File f = File.createTempFile(prefix, suffix, dir); @@ -1345,7 +1390,7 @@ public final class FilePath implements Serializable { * @return true, for a modicum of compatibility */ public boolean delete() throws IOException, InterruptedException { - act(new FileCallable() { + act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public Void invoke(File f, VirtualChannel channel) throws IOException { Util.deleteFile(deleting(f)); @@ -1359,7 +1404,7 @@ public final class FilePath implements Serializable { * Checks if the file exists. */ public boolean exists() throws IOException, InterruptedException { - return act(new FileCallable() { + return act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public Boolean invoke(File f, VirtualChannel channel) throws IOException { return stating(f).exists(); @@ -1375,7 +1420,7 @@ public final class FilePath implements Serializable { * @see #touch(long) */ public long lastModified() throws IOException, InterruptedException { - return act(new FileCallable() { + return act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public Long invoke(File f, VirtualChannel channel) throws IOException { return stating(f).lastModified(); @@ -1389,7 +1434,7 @@ public final class FilePath implements Serializable { * @since 1.299 */ public void touch(final long timestamp) throws IOException, InterruptedException { - act(new FileCallable() { + act(new SecureFileCallable() { private static final long serialVersionUID = -5094638816500738429L; public Void invoke(File f, VirtualChannel channel) throws IOException { if(!f.exists()) @@ -1402,7 +1447,7 @@ public final class FilePath implements Serializable { } private void setLastModifiedIfPossible(final long timestamp) throws IOException, InterruptedException { - String message = act(new FileCallable() { + String message = act(new SecureFileCallable() { private static final long serialVersionUID = -828220335793641630L; public String invoke(File f, VirtualChannel channel) throws IOException { if(!writing(f).setLastModified(timestamp)) { @@ -1427,7 +1472,7 @@ public final class FilePath implements Serializable { * Checks if the file is a directory. */ public boolean isDirectory() throws IOException, InterruptedException { - return act(new FileCallable() { + return act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public Boolean invoke(File f, VirtualChannel channel) throws IOException { return stating(f).isDirectory(); @@ -1441,7 +1486,7 @@ public final class FilePath implements Serializable { * @since 1.129 */ public long length() throws IOException, InterruptedException { - return act(new FileCallable() { + return act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public Long invoke(File f, VirtualChannel channel) throws IOException { return stating(f).length(); @@ -1466,7 +1511,7 @@ public final class FilePath implements Serializable { */ public void chmod(final int mask) throws IOException, InterruptedException { if(!isUnix() || mask==-1) return; - act(new FileCallable() { + act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public Void invoke(File f, VirtualChannel channel) throws IOException { _chmod(writing(f), mask); @@ -1517,7 +1562,7 @@ public final class FilePath implements Serializable { */ public int mode() throws IOException, InterruptedException, PosixException { if(!isUnix()) return -1; - return act(new FileCallable() { + return act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public Integer invoke(File f, VirtualChannel channel) throws IOException { return IOUtils.mode(stating(f)); @@ -1564,7 +1609,7 @@ public final class FilePath implements Serializable { if (filter != null && !(filter instanceof Serializable)) { throw new IllegalArgumentException("Non-serializable filter of " + filter.getClass()); } - return act(new FileCallable>() { + return act(new SecureFileCallable>() { private static final long serialVersionUID = 1L; public List invoke(File f, VirtualChannel channel) throws IOException { File[] children = reading(f).listFiles(filter); @@ -1617,7 +1662,7 @@ public final class FilePath implements Serializable { * @since 1.465 */ public FilePath[] list(final String includes, final String excludes, final boolean defaultExcludes) throws IOException, InterruptedException { - return act(new FileCallable() { + return act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public FilePath[] invoke(File f, VirtualChannel channel) throws IOException { String[] files = glob(reading(f), includes, excludes, defaultExcludes); @@ -1655,7 +1700,7 @@ public final class FilePath implements Serializable { return new FileInputStream(reading(new File(remote))); final Pipe p = Pipe.createRemoteToLocal(); - actAsync(new FileCallable() { + actAsync(new SecureFileCallable() { private static final long serialVersionUID = 1L; @Override @@ -1709,7 +1754,7 @@ public final class FilePath implements Serializable { return new FileOutputStream(writing(f)); } - return act(new FileCallable() { + return act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public OutputStream invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { f = f.getAbsoluteFile(); @@ -1728,7 +1773,7 @@ public final class FilePath implements Serializable { * @since 1.105 */ public void write(final String content, final String encoding) throws IOException, InterruptedException { - act(new FileCallable() { + act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public Void invoke(File f, VirtualChannel channel) throws IOException { mkdirs(f.getParentFile()); @@ -1749,7 +1794,7 @@ public final class FilePath implements Serializable { * @see Util#getDigestOf(File) */ public String digest() throws IOException, InterruptedException { - return act(new FileCallable() { + return act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public String invoke(File f, VirtualChannel channel) throws IOException { return Util.getDigestOf(reading(f)); @@ -1765,7 +1810,7 @@ public final class FilePath implements Serializable { if(this.channel != target.channel) { throw new IOException("renameTo target must be on the same host"); } - act(new FileCallable() { + act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public Void invoke(File f, VirtualChannel channel) throws IOException { reading(f).renameTo(creating(new File(target.remote))); @@ -1783,7 +1828,7 @@ public final class FilePath implements Serializable { if(this.channel != target.channel) { throw new IOException("pullUpTo target must be on the same host"); } - act(new FileCallable() { + act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public Void invoke(File f, VirtualChannel channel) throws IOException { File t = new File(target.getRemote()); @@ -1832,7 +1877,7 @@ public final class FilePath implements Serializable { public void copyTo(OutputStream os) throws IOException, InterruptedException { final OutputStream out = new RemoteOutputStream(os); - act(new FileCallable() { + act(new SecureFileCallable() { private static final long serialVersionUID = 4088559042349254141L; public Void invoke(File f, VirtualChannel channel) throws IOException { FileInputStream fis = null; @@ -1933,7 +1978,7 @@ public final class FilePath implements Serializable { public int copyRecursiveTo(final DirScanner scanner, final FilePath target, final String description) throws IOException, InterruptedException { if(this.channel==target.channel) { // local to local copy. - return act(new FileCallable() { + return act(new SecureFileCallable() { private static final long serialVersionUID = 1L; public Integer invoke(File base, VirtualChannel channel) throws IOException { if(!base.exists()) return 0; @@ -1975,7 +2020,7 @@ public final class FilePath implements Serializable { // local -> remote copy final Pipe pipe = Pipe.createLocalToRemote(); - Future future = target.actAsync(new FileCallable() { + Future future = target.actAsync(new SecureFileCallable() { private static final long serialVersionUID = 1L; public Void invoke(File f, VirtualChannel channel) throws IOException { try { @@ -1986,7 +2031,7 @@ public final class FilePath implements Serializable { } } }); - Future future2 = actAsync(new FileCallable() { + Future future2 = actAsync(new SecureFileCallable() { private static final long serialVersionUID = 1L; @Override public Integer invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { return writeToTar(new File(remote), scanner, TarCompression.GZIP.compress(pipe.getOut())); @@ -2003,7 +2048,7 @@ public final class FilePath implements Serializable { // remote -> local copy final Pipe pipe = Pipe.createRemoteToLocal(); - Future future = actAsync(new FileCallable() { + Future future = actAsync(new SecureFileCallable() { private static final long serialVersionUID = 1L; public Integer invoke(File f, VirtualChannel channel) throws IOException { try { @@ -2126,8 +2171,7 @@ public final class FilePath implements Serializable { return new RemoteLauncher(listener,channel,channel.call(new IsUnix())); } - @MasterToSlave @SlaveToMaster - private static final class IsUnix implements Callable { + private static final class IsUnix extends MasterToSlaveCallable { public Boolean call() throws IOException { return File.pathSeparatorChar==':'; } @@ -2164,7 +2208,7 @@ public final class FilePath implements Serializable { * @since 1.484 */ public String validateAntFileMask(final String fileMasks, final int bound) throws IOException, InterruptedException { - return act(new FileCallable() { + return act(new MasterToSlaveFileCallable() { private static final long serialVersionUID = 1; public String invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException { if(fileMasks.startsWith("~")) @@ -2467,8 +2511,6 @@ public final class FilePath implements Serializable { /** * Adapts {@link FileCallable} to {@link Callable}. */ - @MasterToSlave // the normal case - @SlaveToMaster // possible, but triggers FilePathFilter private class FileCallableWrapper implements DelegatingCallable { private final FileCallable callable; private transient ClassLoader classLoader; @@ -2491,6 +2533,14 @@ public final class FilePath implements Serializable { } } + /** + * Role check comes from {@link FileCallable}s. + */ + @Override + public Collection getRecipients() { + return callable.getRecipients(); + } + public ClassLoader getClassLoader() { return classLoader; } diff --git a/core/src/main/java/hudson/Launcher.java b/core/src/main/java/hudson/Launcher.java index 6f1b4b2205..3e56ed2991 100644 --- a/core/src/main/java/hudson/Launcher.java +++ b/core/src/main/java/hudson/Launcher.java @@ -38,7 +38,7 @@ import hudson.remoting.VirtualChannel; import hudson.util.StreamCopyThread; import hudson.util.ArgumentListBuilder; import hudson.util.ProcessTree; -import jenkins.security.MasterToSlave; +import jenkins.security.MasterToSlaveCallable; import org.apache.commons.io.input.NullInputStream; import java.io.BufferedOutputStream; @@ -888,8 +888,7 @@ public abstract class Launcher { getChannel().call(new KillTask(modelEnvVars)); } - @MasterToSlave - private static final class KillTask implements Callable { + private static final class KillTask extends MasterToSlaveCallable { private final Map modelEnvVars; public KillTask(Map modelEnvVars) { @@ -964,8 +963,7 @@ public abstract class Launcher { IOTriplet getIOtriplet(); } - @MasterToSlave - private static class RemoteLaunchCallable implements Callable { + private static class RemoteLaunchCallable extends MasterToSlaveCallable { private final List cmd; private final boolean[] masks; private final String[] env; @@ -1035,8 +1033,7 @@ public abstract class Launcher { private static final long serialVersionUID = 1L; } - @MasterToSlave - private static class RemoteChannelLaunchCallable implements Callable { + private static class RemoteChannelLaunchCallable extends MasterToSlaveCallable { private final String[] cmd; private final Pipe out; private final String workDir; diff --git a/core/src/main/java/hudson/cli/CLICommand.java b/core/src/main/java/hudson/cli/CLICommand.java index 0efb64f3f9..aa41dbf425 100644 --- a/core/src/main/java/hudson/cli/CLICommand.java +++ b/core/src/main/java/hudson/cli/CLICommand.java @@ -36,8 +36,7 @@ import hudson.remoting.Channel; import hudson.remoting.ChannelProperty; import hudson.security.CliAuthenticator; import hudson.security.SecurityRealm; -import jenkins.security.MasterToSlave; -import jenkins.security.SlaveToMaster; +import jenkins.security.MasterToSlaveCallable; import org.acegisecurity.Authentication; import org.acegisecurity.BadCredentialsException; import org.acegisecurity.context.SecurityContext; @@ -361,8 +360,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable { return checkChannel().call(new GetSystemProperty(name)); } - @MasterToSlave - private static final class GetSystemProperty implements Callable { + private static final class GetSystemProperty extends MasterToSlaveCallable { private final String name; private GetSystemProperty(String name) { @@ -391,8 +389,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable { } } - @MasterToSlave @SlaveToMaster - private static final class GetCharset implements Callable { + private static final class GetCharset extends MasterToSlaveCallable { public String call() throws IOException { return Charset.defaultCharset().name(); } @@ -407,8 +404,7 @@ public abstract class CLICommand implements ExtensionPoint, Cloneable { return checkChannel().call(new GetEnvironmentVariable(name)); } - @MasterToSlave - private static final class GetEnvironmentVariable implements Callable { + private static final class GetEnvironmentVariable extends MasterToSlaveCallable { private final String name; private GetEnvironmentVariable(String name) { diff --git a/core/src/main/java/hudson/cli/CommandDuringBuild.java b/core/src/main/java/hudson/cli/CommandDuringBuild.java index 759563744a..1ebaad75a3 100644 --- a/core/src/main/java/hudson/cli/CommandDuringBuild.java +++ b/core/src/main/java/hudson/cli/CommandDuringBuild.java @@ -28,7 +28,7 @@ import jenkins.model.Jenkins; import hudson.model.Job; import hudson.model.Run; import hudson.remoting.Callable; -import jenkins.security.MasterToSlave; +import jenkins.security.MasterToSlaveCallable; import org.kohsuke.args4j.CmdLineException; import java.io.IOException; @@ -82,8 +82,7 @@ public abstract class CommandDuringBuild extends CLICommand { /** * Gets the environment variables that points to the build being executed. */ - @MasterToSlave - private static final class GetCharacteristicEnvironmentVariables implements Callable { + private static final class GetCharacteristicEnvironmentVariables extends MasterToSlaveCallable { public String[] call() throws IOException { return new String[] { System.getenv("JOB_NAME"), diff --git a/core/src/main/java/hudson/cli/InstallToolCommand.java b/core/src/main/java/hudson/cli/InstallToolCommand.java index e39d90fba4..a5dd319335 100644 --- a/core/src/main/java/hudson/cli/InstallToolCommand.java +++ b/core/src/main/java/hudson/cli/InstallToolCommand.java @@ -31,10 +31,7 @@ import hudson.model.AbstractProject; import hudson.model.Run; import hudson.model.Executor; import hudson.model.Node; -import hudson.model.EnvironmentSpecific; import hudson.model.Item; -import hudson.remoting.Callable; -import hudson.slaves.NodeSpecific; import hudson.util.EditDistance; import hudson.util.StreamTaskListener; import hudson.tools.ToolDescriptor; @@ -44,7 +41,7 @@ import java.util.List; import java.util.ArrayList; import java.io.IOException; -import jenkins.security.MasterToSlave; +import jenkins.security.MasterToSlaveCallable; import org.kohsuke.args4j.Argument; /** @@ -129,8 +126,7 @@ public class InstallToolCommand extends CLICommand { return 0; } - @MasterToSlave - private static final class BuildIDs implements Callable { + private static final class BuildIDs extends MasterToSlaveCallable { String job,number,id; public BuildIDs call() throws IOException { diff --git a/core/src/main/java/hudson/cli/util/ScriptLoader.java b/core/src/main/java/hudson/cli/util/ScriptLoader.java index 5b590ac313..e2252efbd5 100644 --- a/core/src/main/java/hudson/cli/util/ScriptLoader.java +++ b/core/src/main/java/hudson/cli/util/ScriptLoader.java @@ -1,8 +1,7 @@ package hudson.cli.util; import hudson.AbortException; -import hudson.remoting.Callable; -import jenkins.security.MasterToSlave; +import jenkins.security.MasterToSlaveCallable; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; @@ -17,8 +16,7 @@ import java.net.URL; * * @author vjuranek */ -@MasterToSlave -public class ScriptLoader implements Callable { +public class ScriptLoader extends MasterToSlaveCallable { private final String script; diff --git a/core/src/main/java/hudson/logging/LogRecorder.java b/core/src/main/java/hudson/logging/LogRecorder.java index 9cc456f9f4..dae28a2c53 100644 --- a/core/src/main/java/hudson/logging/LogRecorder.java +++ b/core/src/main/java/hudson/logging/LogRecorder.java @@ -42,7 +42,7 @@ import hudson.slaves.ComputerListener; import hudson.util.CopyOnWriteList; import hudson.util.RingBufferLogHandler; import hudson.util.XStream2; -import jenkins.security.MasterToSlave; +import jenkins.security.MasterToSlaveCallable; import net.sf.json.JSONObject; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; @@ -161,8 +161,7 @@ public class LogRecorder extends AbstractModelObject implements Saveable { } - @MasterToSlave - private static final class SetLevel implements Callable { + private static final class SetLevel extends MasterToSlaveCallable { /** known loggers (kept per slave), to avoid GC */ @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") private static final Set loggers = new HashSet(); private final String name; diff --git a/core/src/main/java/hudson/model/AbstractItem.java b/core/src/main/java/hudson/model/AbstractItem.java index 15de44afa6..cd9afb1331 100644 --- a/core/src/main/java/hudson/model/AbstractItem.java +++ b/core/src/main/java/hudson/model/AbstractItem.java @@ -34,7 +34,6 @@ import hudson.cli.declarative.CLIMethod; import hudson.cli.declarative.CLIResolver; import hudson.model.listeners.ItemListener; import hudson.model.listeners.SaveableListener; -import hudson.remoting.Callable; import hudson.security.AccessControlled; import hudson.security.Permission; import hudson.security.ACL; @@ -44,6 +43,7 @@ import hudson.util.AtomicFileWriter; import hudson.util.IOException2; import hudson.util.IOUtils; import jenkins.model.Jenkins; +import jenkins.security.NotReallyRoleSensitiveCallable; import org.acegisecurity.Authentication; import org.apache.tools.ant.taskdefs.Copy; import org.apache.tools.ant.types.FileSet; @@ -216,7 +216,7 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet // the test to see if the project already exists or not needs to be done in escalated privilege // to avoid overwriting - ACL.impersonate(ACL.SYSTEM,new Callable() { + ACL.impersonate(ACL.SYSTEM,new NotReallyRoleSensitiveCallable() { final Authentication user = Jenkins.getAuthentication(); @Override public Void call() throws IOException { diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index 42c2c76b89..5d1a60f075 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -58,7 +58,7 @@ import hudson.util.RemotingDiagnostics.HeapDump; import hudson.util.RunList; import hudson.util.Futures; import jenkins.model.Jenkins; -import jenkins.security.MasterToSlave; +import jenkins.security.MasterToSlaveCallable; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.stapler.StaplerRequest; @@ -1025,8 +1025,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces oneOffExecutors.remove(e); } - @MasterToSlave - private static class ListPossibleNames implements Callable,IOException> { + private static class ListPossibleNames extends MasterToSlaveCallable,IOException> { public List call() throws IOException { List names = new ArrayList(); @@ -1056,8 +1055,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces private static final long serialVersionUID = 1L; } - @MasterToSlave - private static class GetFallbackName implements Callable { + private static class GetFallbackName extends MasterToSlaveCallable { public String call() throws IOException { return System.getProperty("host.name"); } @@ -1146,8 +1144,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces w.close(); } - @MasterToSlave - private static final class DumpExportTableTask implements Callable { + private static final class DumpExportTableTask extends MasterToSlaveCallable { public String call() throws IOException { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); diff --git a/core/src/main/java/hudson/model/Slave.java b/core/src/main/java/hudson/model/Slave.java index 53d429379b..20a494bc49 100644 --- a/core/src/main/java/hudson/model/Slave.java +++ b/core/src/main/java/hudson/model/Slave.java @@ -58,7 +58,7 @@ import javax.servlet.ServletException; import hudson.util.TimeUnit2; import jenkins.model.Jenkins; -import jenkins.security.MasterToSlave; +import jenkins.security.MasterToSlaveCallable; import jenkins.slaves.WorkspaceLocator; import org.apache.commons.io.IOUtils; @@ -439,8 +439,7 @@ public abstract class Slave extends Node implements Serializable { *

  • When it's read on this side as a return value, it morphs itself into {@link ClockDifference}. * */ - @MasterToSlave - private static final class GetClockDifference1 implements Callable { + private static final class GetClockDifference1 extends MasterToSlaveCallable { public ClockDifference call() { // this method must be being invoked locally, which means the clock is in sync return new ClockDifference(0); @@ -453,8 +452,7 @@ public abstract class Slave extends Node implements Serializable { private static final long serialVersionUID = 1L; } - @MasterToSlave - private static final class GetClockDifference2 implements Callable { + private static final class GetClockDifference2 extends MasterToSlaveCallable { /** * Capture the time on the master when this object is sent to remote, which is when * {@link GetClockDifference1#writeReplace()} is run. @@ -468,7 +466,6 @@ public abstract class Slave extends Node implements Serializable { private static final long serialVersionUID = 1L; } - @MasterToSlave private static final class GetClockDifference3 implements Serializable { private final long remoteTime = System.currentTimeMillis(); private final long startTime; diff --git a/core/src/main/java/hudson/node_monitors/ArchitectureMonitor.java b/core/src/main/java/hudson/node_monitors/ArchitectureMonitor.java index 05c59a91dd..ec0618f807 100644 --- a/core/src/main/java/hudson/node_monitors/ArchitectureMonitor.java +++ b/core/src/main/java/hudson/node_monitors/ArchitectureMonitor.java @@ -26,7 +26,7 @@ package hudson.node_monitors; import hudson.model.Computer; import hudson.remoting.Callable; import hudson.Extension; -import jenkins.security.MasterToSlave; +import jenkins.security.MasterToSlaveCallable; import net.sf.json.JSONObject; import org.kohsuke.stapler.StaplerRequest; @@ -57,8 +57,7 @@ public class ArchitectureMonitor extends NodeMonitor { /** * Obtains the string that represents the architecture. */ - @MasterToSlave - private static class GetArchTask implements Callable { + private static class GetArchTask extends MasterToSlaveCallable { public String call() { String os = System.getProperty("os.name"); String arch = System.getProperty("os.arch"); diff --git a/core/src/main/java/hudson/node_monitors/DiskSpaceMonitorDescriptor.java b/core/src/main/java/hudson/node_monitors/DiskSpaceMonitorDescriptor.java index 8f1fb84995..4d53f720b5 100644 --- a/core/src/main/java/hudson/node_monitors/DiskSpaceMonitorDescriptor.java +++ b/core/src/main/java/hudson/node_monitors/DiskSpaceMonitorDescriptor.java @@ -23,8 +23,7 @@ */ package hudson.node_monitors; -import hudson.FilePath.FileCallable; -import hudson.model.Computer; +import hudson.FilePath.MasterToSlaveFileCallable; import hudson.remoting.VirtualChannel; import hudson.Util; import hudson.slaves.OfflineCause; @@ -159,7 +158,7 @@ public abstract class DiskSpaceMonitorDescriptor extends AbstractAsyncNodeMonito private static final long serialVersionUID = 2L; } - protected static final class GetUsableSpace implements FileCallable { + protected static final class GetUsableSpace extends MasterToSlaveFileCallable { public GetUsableSpace() {} @IgnoreJRERequirement public DiskSpace invoke(File f, VirtualChannel channel) throws IOException { diff --git a/core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java b/core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java index 491b991e7a..85784327a2 100644 --- a/core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java +++ b/core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java @@ -32,8 +32,7 @@ import hudson.remoting.Future; import hudson.util.ClockDifference; import hudson.util.TimeUnit2; import hudson.util.IOException2; -import jenkins.security.MasterToSlave; -import jenkins.security.SlaveToMaster; +import jenkins.security.MasterToSlaveCallable; import net.sf.json.JSONObject; import org.kohsuke.stapler.StaplerRequest; @@ -94,8 +93,7 @@ public class ResponseTimeMonitor extends NodeMonitor { } }; - @MasterToSlave - private static final class Step1 implements Callable { + private static final class Step1 extends MasterToSlaveCallable { private Data cur; private Step1(Data cur) { @@ -114,8 +112,7 @@ public class ResponseTimeMonitor extends NodeMonitor { private static final long serialVersionUID = 1L; } - @MasterToSlave - private static final class Step2 implements Callable { + private static final class Step2 extends MasterToSlaveCallable { private final Data cur; private final long start = System.currentTimeMillis(); @@ -131,7 +128,6 @@ public class ResponseTimeMonitor extends NodeMonitor { private static final long serialVersionUID = 1L; } - @SlaveToMaster private static final class Step3 implements Serializable { private final Data cur; private final long start; diff --git a/core/src/main/java/hudson/node_monitors/SwapSpaceMonitor.java b/core/src/main/java/hudson/node_monitors/SwapSpaceMonitor.java index a72b94e84d..5efcc974bc 100644 --- a/core/src/main/java/hudson/node_monitors/SwapSpaceMonitor.java +++ b/core/src/main/java/hudson/node_monitors/SwapSpaceMonitor.java @@ -27,8 +27,7 @@ import hudson.Util; import hudson.Extension; import hudson.model.Computer; import jenkins.model.Jenkins; -import hudson.remoting.Callable; -import jenkins.security.MasterToSlave; +import jenkins.security.MasterToSlaveCallable; import net.sf.json.JSONObject; import org.jvnet.hudson.MemoryMonitor; import org.jvnet.hudson.MemoryUsage; @@ -98,8 +97,7 @@ public class SwapSpaceMonitor extends NodeMonitor { /** * Obtains the string that represents the architecture. */ - @MasterToSlave - private static class MonitorTask implements Callable { + private static class MonitorTask extends MasterToSlaveCallable { public MemoryUsage call() throws IOException { MemoryMonitor mm; try { diff --git a/core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java b/core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java index 05407d318f..707c043c5f 100644 --- a/core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java +++ b/core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java @@ -25,7 +25,7 @@ package hudson.node_monitors; import hudson.Extension; import hudson.FilePath; -import hudson.FilePath.FileCallable; +import hudson.FilePath.MasterToSlaveFileCallable; import hudson.Functions; import hudson.model.Computer; import hudson.remoting.Callable; @@ -82,7 +82,7 @@ public class TemporarySpaceMonitor extends AbstractDiskSpaceMonitor { return null; } - protected static final class GetTempSpace implements FileCallable { + protected static final class GetTempSpace extends MasterToSlaveFileCallable { @IgnoreJRERequirement public DiskSpace invoke(File f, VirtualChannel channel) throws IOException { try { diff --git a/core/src/main/java/hudson/os/solaris/ZFSProvisioner.java b/core/src/main/java/hudson/os/solaris/ZFSProvisioner.java index 3cae513ffa..b2f5a4443a 100644 --- a/core/src/main/java/hudson/os/solaris/ZFSProvisioner.java +++ b/core/src/main/java/hudson/os/solaris/ZFSProvisioner.java @@ -23,13 +23,13 @@ */ package hudson.os.solaris; +import hudson.FilePath.MasterToSlaveFileCallable; import hudson.FileSystemProvisioner; import hudson.FilePath; import hudson.WorkspaceSnapshot; import hudson.FileSystemProvisionerDescriptor; import hudson.Extension; import hudson.remoting.VirtualChannel; -import hudson.FilePath.FileCallable; import hudson.model.AbstractBuild; import hudson.model.TaskListener; import hudson.model.AbstractProject; @@ -52,7 +52,7 @@ public class ZFSProvisioner extends FileSystemProvisioner implements Serializabl private final String rootDataset; public ZFSProvisioner(Node node) throws IOException, InterruptedException { - rootDataset = node.getRootPath().act(new FileCallable() { + rootDataset = node.getRootPath().act(new MasterToSlaveFileCallable() { private static final long serialVersionUID = -2142349338699797436L; public String invoke(File f, VirtualChannel channel) throws IOException { @@ -68,7 +68,7 @@ public class ZFSProvisioner extends FileSystemProvisioner implements Serializabl public void prepareWorkspace(AbstractBuild build, FilePath ws, final TaskListener listener) throws IOException, InterruptedException { final String name = build.getProject().getFullName(); - ws.act(new FileCallable() { + ws.act(new MasterToSlaveFileCallable() { private static final long serialVersionUID = 2129531727963121198L; public Void invoke(File f, VirtualChannel channel) throws IOException { @@ -87,7 +87,7 @@ public class ZFSProvisioner extends FileSystemProvisioner implements Serializabl } public void discardWorkspace(AbstractProject project, FilePath ws) throws IOException, InterruptedException { - ws.act(new FileCallable() { + ws.act(new MasterToSlaveFileCallable() { private static final long serialVersionUID = 1916618107019257530L; public Void invoke(File f, VirtualChannel channel) throws IOException { diff --git a/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java b/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java index 84e9b163d6..eeff6baa95 100644 --- a/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java +++ b/core/src/main/java/hudson/security/AbstractPasswordBasedSecurityRealm.java @@ -4,9 +4,8 @@ import groovy.lang.Binding; import hudson.FilePath; import hudson.cli.CLICommand; import jenkins.model.Jenkins; -import hudson.remoting.Callable; import hudson.util.spring.BeanBuilder; -import jenkins.security.MasterToSlave; +import jenkins.security.MasterToSlaveCallable; import org.acegisecurity.Authentication; import org.acegisecurity.AuthenticationException; import org.acegisecurity.AuthenticationManager; @@ -141,8 +140,7 @@ public abstract class AbstractPasswordBasedSecurityRealm extends SecurityRealm i /** * Asks for the password. */ - @MasterToSlave - private static class InteractivelyAskForPassword implements Callable { + private static class InteractivelyAskForPassword extends MasterToSlaveCallable { @IgnoreJRERequirement public String call() throws IOException { Console console = System.console(); diff --git a/core/src/main/java/hudson/slaves/ChannelPinger.java b/core/src/main/java/hudson/slaves/ChannelPinger.java index f7de0f0349..bf3b4d3034 100644 --- a/core/src/main/java/hudson/slaves/ChannelPinger.java +++ b/core/src/main/java/hudson/slaves/ChannelPinger.java @@ -28,10 +28,9 @@ import hudson.FilePath; import hudson.model.Computer; import hudson.model.Slave; import hudson.model.TaskListener; -import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.remoting.PingThread; -import jenkins.security.MasterToSlave; +import jenkins.security.MasterToSlaveCallable; import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; @@ -86,8 +85,7 @@ public class ChannelPinger extends ComputerListener { setUpPingForChannel(channel, pingInterval); } - @MasterToSlave - private static class SetUpRemotePing implements Callable { + private static class SetUpRemotePing extends MasterToSlaveCallable { private static final long serialVersionUID = -2702219700841759872L; private int pingInterval; public SetUpRemotePing(int pingInterval) { diff --git a/core/src/main/java/hudson/slaves/ConnectionActivityMonitor.java b/core/src/main/java/hudson/slaves/ConnectionActivityMonitor.java index 997a47525f..41d9aaa431 100644 --- a/core/src/main/java/hudson/slaves/ConnectionActivityMonitor.java +++ b/core/src/main/java/hudson/slaves/ConnectionActivityMonitor.java @@ -30,10 +30,8 @@ import hudson.model.Computer; import hudson.util.TimeUnit2; import hudson.remoting.VirtualChannel; import hudson.remoting.Channel; -import hudson.remoting.Callable; import hudson.Extension; -import jenkins.security.MasterToSlave; -import jenkins.security.SlaveToMaster; +import jenkins.security.SlaveToMasterCallable; import java.io.IOException; import java.util.logging.Logger; @@ -104,8 +102,7 @@ public class ConnectionActivityMonitor extends AsyncPeriodicWork { public boolean enabled = Boolean.getBoolean(ConnectionActivityMonitor.class.getName()+".enabled"); private static final PingCommand PING_COMMAND = new PingCommand(); - @MasterToSlave @SlaveToMaster - private static final class PingCommand implements Callable { + private static final class PingCommand extends SlaveToMasterCallable { public Void call() throws RuntimeException { return null; } diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java index 1b7348128b..1b1517bd46 100644 --- a/core/src/main/java/hudson/slaves/SlaveComputer.java +++ b/core/src/main/java/hudson/slaves/SlaveComputer.java @@ -72,8 +72,7 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import javax.servlet.RequestDispatcher; import jenkins.model.Jenkins; -import jenkins.security.MasterToSlave; -import jenkins.security.SlaveToMaster; +import jenkins.security.MasterToSlaveCallable; import jenkins.slaves.JnlpSlaveAgentProtocol; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -401,8 +400,7 @@ public class SlaveComputer extends Computer { return channel.call(new LoadingTime(true)); } - @MasterToSlave @SlaveToMaster - static class LoadingCount implements Callable { + static class LoadingCount extends MasterToSlaveCallable { private final boolean resource; LoadingCount(boolean resource) { this.resource = resource; @@ -413,15 +411,13 @@ public class SlaveComputer extends Computer { } } - @MasterToSlave - static class LoadingPrefetchCacheCount implements Callable { + static class LoadingPrefetchCacheCount extends MasterToSlaveCallable { @Override public Integer call() { return Channel.current().classLoadingPrefetchCacheCount.get(); } } - @MasterToSlave - static class LoadingTime implements Callable { + static class LoadingTime extends MasterToSlaveCallable { private final boolean resource; LoadingTime(boolean resource) { this.resource = resource; @@ -716,22 +712,19 @@ public class SlaveComputer extends Computer { private static final Logger logger = Logger.getLogger(SlaveComputer.class.getName()); - @MasterToSlave - private static final class SlaveVersion implements Callable { + private static final class SlaveVersion extends MasterToSlaveCallable { public String call() throws IOException { try { return Launcher.VERSION; } catch (Throwable ex) { return "< 1.335"; } // Older slave.jar won't have VERSION } } - @MasterToSlave @SlaveToMaster - private static final class DetectOS implements Callable { + private static final class DetectOS extends MasterToSlaveCallable { public Boolean call() throws IOException { return File.pathSeparatorChar==':'; } } - @MasterToSlave @SlaveToMaster - private static final class DetectDefaultCharset implements Callable { + private static final class DetectDefaultCharset extends MasterToSlaveCallable { public String call() throws IOException { return Charset.defaultCharset().name(); } @@ -748,8 +741,7 @@ public class SlaveComputer extends Computer { static final RingBufferLogHandler SLAVE_LOG_HANDLER = new RingBufferLogHandler(); } - @MasterToSlave - private static class SlaveInitializer implements Callable { + private static class SlaveInitializer extends MasterToSlaveCallable { public Void call() { // avoid double installation of the handler. JNLP slaves can reconnect to the master multiple times // and each connection gets a different RemoteClassLoader, so we need to evict them by class name, @@ -796,8 +788,7 @@ public class SlaveComputer extends Computer { return null; } - @MasterToSlave - private static class SlaveLogFetcher implements Callable,RuntimeException> { + private static class SlaveLogFetcher extends MasterToSlaveCallable,RuntimeException> { public List call() { return new ArrayList(SLAVE_LOG_HANDLER.getView()); } diff --git a/core/src/main/java/hudson/tasks/ArtifactArchiver.java b/core/src/main/java/hudson/tasks/ArtifactArchiver.java index 7faa7b8986..a78aae14b2 100644 --- a/core/src/main/java/hudson/tasks/ArtifactArchiver.java +++ b/core/src/main/java/hudson/tasks/ArtifactArchiver.java @@ -24,6 +24,7 @@ package hudson.tasks; import hudson.FilePath; +import hudson.FilePath.MasterToSlaveFileCallable; import hudson.Launcher; import hudson.Util; import hudson.Extension; @@ -169,7 +170,7 @@ public class ArtifactArchiver extends Recorder { return true; } - private static final class ListFiles implements FilePath.FileCallable> { + private static final class ListFiles extends MasterToSlaveFileCallable> { private static final long serialVersionUID = 1; private final String includes, excludes; ListFiles(String includes, String excludes) { diff --git a/core/src/main/java/hudson/tasks/Fingerprinter.java b/core/src/main/java/hudson/tasks/Fingerprinter.java index f39828708b..a5a378b3f3 100644 --- a/core/src/main/java/hudson/tasks/Fingerprinter.java +++ b/core/src/main/java/hudson/tasks/Fingerprinter.java @@ -28,6 +28,7 @@ import hudson.EnvVars; import hudson.Extension; import hudson.FilePath; import hudson.FilePath.FileCallable; +import hudson.FilePath.MasterToSlaveFileCallable; import hudson.Launcher; import hudson.Util; import hudson.matrix.MatrixConfiguration; @@ -220,7 +221,7 @@ public class Fingerprinter extends Recorder implements Serializable, DependencyD return; } - List records = ws.act(new FileCallable>() { + List records = ws.act(new MasterToSlaveFileCallable>() { public List invoke(File baseDir, VirtualChannel channel) throws IOException { List results = new ArrayList(); diff --git a/core/src/main/java/hudson/tasks/Maven.java b/core/src/main/java/hudson/tasks/Maven.java index aed029341e..95c3f6f61e 100644 --- a/core/src/main/java/hudson/tasks/Maven.java +++ b/core/src/main/java/hudson/tasks/Maven.java @@ -24,6 +24,7 @@ package hudson.tasks; import hudson.Extension; +import hudson.FilePath.MasterToSlaveFileCallable; import hudson.Launcher; import hudson.Functions; import hudson.EnvVars; @@ -219,7 +220,7 @@ public class Maven extends Builder { * Looks for pom.xlm or project.xml to determine the maven executable * name. */ - private static final class DecideDefaultMavenCommand implements FileCallable { + private static final class DecideDefaultMavenCommand extends MasterToSlaveFileCallable { private static final long serialVersionUID = -2327576423452215146L; // command line arguments. private final String arguments; diff --git a/core/src/main/java/hudson/tasks/Shell.java b/core/src/main/java/hudson/tasks/Shell.java index 64c0192f6f..e7283accf3 100644 --- a/core/src/main/java/hudson/tasks/Shell.java +++ b/core/src/main/java/hudson/tasks/Shell.java @@ -28,12 +28,11 @@ import hudson.Functions; import hudson.Util; import hudson.Extension; import hudson.model.AbstractProject; -import hudson.remoting.Callable; import hudson.remoting.VirtualChannel; import hudson.util.FormValidation; import java.io.IOException; -import jenkins.security.MasterToSlave; +import jenkins.security.MasterToSlaveCallable; import net.sf.json.JSONObject; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; @@ -195,8 +194,7 @@ public class Shell extends CommandInterpreter { return FormValidation.validateExecutable(value); } - @MasterToSlave - private static final class Shellinterpreter implements Callable { + private static final class Shellinterpreter extends MasterToSlaveCallable { private static final long serialVersionUID = 1L; diff --git a/core/src/main/java/hudson/tasks/junit/JUnitParser.java b/core/src/main/java/hudson/tasks/junit/JUnitParser.java index 97b02f4fac..d72944263d 100644 --- a/core/src/main/java/hudson/tasks/junit/JUnitParser.java +++ b/core/src/main/java/hudson/tasks/junit/JUnitParser.java @@ -23,6 +23,7 @@ */ package hudson.tasks.junit; +import hudson.FilePath.MasterToSlaveFileCallable; import hudson.model.TaskListener; import hudson.tasks.test.TestResultParser; import hudson.model.AbstractBuild; @@ -87,8 +88,8 @@ public class JUnitParser extends TestResultParser { return workspace.act(new ParseResultCallable(testResultLocations, buildTime, timeOnMaster, keepLongStdio)); } - private static final class ParseResultCallable implements - FilePath.FileCallable { + private static final class ParseResultCallable extends + MasterToSlaveFileCallable { private final long buildTime; private final String testResults; private final long nowMaster; diff --git a/core/src/main/java/hudson/tasks/test/DefaultTestResultParserImpl.java b/core/src/main/java/hudson/tasks/test/DefaultTestResultParserImpl.java index ffd6db2859..ab364c0566 100644 --- a/core/src/main/java/hudson/tasks/test/DefaultTestResultParserImpl.java +++ b/core/src/main/java/hudson/tasks/test/DefaultTestResultParserImpl.java @@ -25,7 +25,7 @@ package hudson.tasks.test; import hudson.AbortException; import hudson.FilePath; -import hudson.FilePath.FileCallable; +import hudson.FilePath.MasterToSlaveFileCallable; import hudson.Launcher; import hudson.Util; import hudson.model.AbstractBuild; @@ -74,7 +74,7 @@ public abstract class DefaultTestResultParserImpl extends TestResultParser imple @Override public TestResult parse(final String testResultLocations, final AbstractBuild build, final Launcher launcher, final TaskListener listener) throws InterruptedException, IOException { - return build.getWorkspace().act(new FileCallable() { + return build.getWorkspace().act(new MasterToSlaveFileCallable() { final boolean ignoreTimestampCheck = IGNORE_TIMESTAMP_CHECK; // so that the property can be set on the master final long buildTime = build.getTimestamp().getTimeInMillis(); final long nowMaster = System.currentTimeMillis(); diff --git a/core/src/main/java/hudson/tools/JDKInstaller.java b/core/src/main/java/hudson/tools/JDKInstaller.java index 5bb4e377ef..3e5353b5ff 100644 --- a/core/src/main/java/hudson/tools/JDKInstaller.java +++ b/core/src/main/java/hudson/tools/JDKInstaller.java @@ -40,7 +40,7 @@ import hudson.util.FormValidation; import hudson.util.HttpResponses; import hudson.util.Secret; import jenkins.model.Jenkins; -import jenkins.security.MasterToSlave; +import jenkins.security.MasterToSlaveCallable; import net.sf.json.JSONObject; import org.apache.commons.httpclient.Cookie; import org.apache.commons.httpclient.HttpClient; @@ -502,8 +502,7 @@ public class JDKInstaller extends ToolInstaller { throw new DetectionFailedException("Unknown CPU name: "+arch); } - @MasterToSlave - static class GetCurrentPlatform implements Callable { + static class GetCurrentPlatform extends MasterToSlaveCallable { private static final long serialVersionUID = 1L; public Platform call() throws DetectionFailedException { return current(); @@ -565,8 +564,7 @@ public class JDKInstaller extends ToolInstaller { throw new DetectionFailedException("Unknown CPU architecture: "+arch); } - @MasterToSlave - static class GetCurrentCPU implements Callable { + static class GetCurrentCPU extends MasterToSlaveCallable { private static final long serialVersionUID = 1L; public CPU call() throws DetectionFailedException { return current(); diff --git a/core/src/main/java/hudson/tools/ZipExtractionInstaller.java b/core/src/main/java/hudson/tools/ZipExtractionInstaller.java index 48ef62b97c..ff3b3837ec 100644 --- a/core/src/main/java/hudson/tools/ZipExtractionInstaller.java +++ b/core/src/main/java/hudson/tools/ZipExtractionInstaller.java @@ -26,7 +26,7 @@ package hudson.tools; import hudson.Extension; import hudson.FilePath; -import hudson.FilePath.FileCallable; +import hudson.FilePath.MasterToSlaveFileCallable; import hudson.ProxyConfiguration; import hudson.Util; import hudson.Functions; @@ -119,7 +119,7 @@ public class ZipExtractionInstaller extends ToolInstaller { * Sets execute permission on all files, since unzip etc. might not do this. * Hackish, is there a better way? */ - static class ChmodRecAPlusX implements FileCallable { + static class ChmodRecAPlusX extends MasterToSlaveFileCallable { private static final long serialVersionUID = 1L; public Void invoke(File d, VirtualChannel channel) throws IOException { if(!Functions.isWindows()) diff --git a/core/src/main/java/hudson/util/RemotingDiagnostics.java b/core/src/main/java/hudson/util/RemotingDiagnostics.java index a0edd54abf..5dc4c430a5 100644 --- a/core/src/main/java/hudson/util/RemotingDiagnostics.java +++ b/core/src/main/java/hudson/util/RemotingDiagnostics.java @@ -34,7 +34,7 @@ import hudson.remoting.DelegatingCallable; import hudson.remoting.Future; import hudson.remoting.VirtualChannel; import hudson.security.AccessControlled; -import jenkins.security.MasterToSlave; +import jenkins.security.MasterToSlaveCallable; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.ImportCustomizer; import org.kohsuke.stapler.StaplerRequest; @@ -71,8 +71,7 @@ public final class RemotingDiagnostics { return channel.call(new GetSystemProperties()); } - @MasterToSlave - private static final class GetSystemProperties implements Callable,RuntimeException> { + private static final class GetSystemProperties extends MasterToSlaveCallable,RuntimeException> { public Map call() { return new TreeMap(System.getProperties()); } @@ -91,8 +90,7 @@ public final class RemotingDiagnostics { return channel.callAsync(new GetThreadDump()); } - @MasterToSlave - private static final class GetThreadDump implements Callable,RuntimeException> { + private static final class GetThreadDump extends MasterToSlaveCallable,RuntimeException> { public Map call() { Map r = new LinkedHashMap(); try { @@ -122,8 +120,7 @@ public final class RemotingDiagnostics { return channel.call(new Script(script)); } - @MasterToSlave - private static final class Script implements DelegatingCallable { + private static final class Script extends MasterToSlaveCallable implements DelegatingCallable { private final String script; private transient ClassLoader cl; @@ -205,8 +202,7 @@ public final class RemotingDiagnostics { } } - @MasterToSlave - private static class GetHeapDump implements Callable { + private static class GetHeapDump extends MasterToSlaveCallable { public FilePath call() throws IOException { final File hprof = File.createTempFile("hudson-heapdump", "hprof"); hprof.delete(); diff --git a/core/src/main/java/jenkins/security/MasterToSlave.java b/core/src/main/java/jenkins/security/MasterToSlave.java deleted file mode 100644 index 832af307a3..0000000000 --- a/core/src/main/java/jenkins/security/MasterToSlave.java +++ /dev/null @@ -1,25 +0,0 @@ -package jenkins.security; - -import hudson.remoting.Callable; - -import java.lang.annotation.Documented; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * Annotates {@link Callable} that this callable is meant to be run on the slave. - * - * @author Kohsuke Kawaguchi - * @see SlaveToMaster - * @since 1.THU - */ -@Retention(RUNTIME) -@Target(TYPE) -@Inherited -@Documented -public @interface MasterToSlave { -} diff --git a/core/src/main/java/jenkins/security/MasterToSlaveCallable.java b/core/src/main/java/jenkins/security/MasterToSlaveCallable.java index 1834a08560..1d498d290e 100644 --- a/core/src/main/java/jenkins/security/MasterToSlaveCallable.java +++ b/core/src/main/java/jenkins/security/MasterToSlaveCallable.java @@ -1,14 +1,21 @@ package jenkins.security; import hudson.remoting.Callable; +import org.jenkinsci.remoting.Role; + +import java.util.Collection; /** - * Convenient {@link Callable} with {@link MasterToSlave} to create anonymous Callable class. + * Convenient {@link Callable} meant to be run on slave. * * @author Kohsuke Kawaguchi * @since 1.THU */ -@MasterToSlave public abstract class MasterToSlaveCallable implements Callable { + @Override + public Collection getRecipients() { + return Roles.FOR_SLAVE; + } + private static final long serialVersionUID = 1L; } diff --git a/core/src/main/java/jenkins/security/NotReallyRoleSensitiveCallable.java b/core/src/main/java/jenkins/security/NotReallyRoleSensitiveCallable.java new file mode 100644 index 0000000000..5663cd7575 --- /dev/null +++ b/core/src/main/java/jenkins/security/NotReallyRoleSensitiveCallable.java @@ -0,0 +1,21 @@ +package jenkins.security; + +import hudson.remoting.Callable; +import org.jenkinsci.remoting.Role; + +import java.util.Collection; + +/** + * {@link Callable} adapter for situations where Callable is not used for remoting but + * just as a convenient function that has parameterized return value and exception type. + * + * @author Kohsuke Kawaguchi + * @since 1.THU + */ +public abstract class NotReallyRoleSensitiveCallable implements Callable { + @Override + public final Collection getRecipients() { + // not meant to be used where this matters + throw new UnsupportedOperationException(); + } +} diff --git a/core/src/main/java/jenkins/security/SlaveToMaster.java b/core/src/main/java/jenkins/security/SlaveToMaster.java deleted file mode 100644 index d5ececbd22..0000000000 --- a/core/src/main/java/jenkins/security/SlaveToMaster.java +++ /dev/null @@ -1,26 +0,0 @@ -package jenkins.security; - -import hudson.remoting.Callable; - -import java.lang.annotation.Documented; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.*; - -/** - * Annotates {@link Callable} that are meant for the slave to execute on the master. - * - * @author Kohsuke Kawaguchi - * @see MasterToSlave - * @see CallableDirectionChecker - * @since 1.THU - */ -@Retention(RUNTIME) -@Target(TYPE) -@Inherited -@Documented -public @interface SlaveToMaster { -} diff --git a/core/src/main/java/jenkins/security/SlaveToMasterCallable.java b/core/src/main/java/jenkins/security/SlaveToMasterCallable.java index 0a05dc22d8..cb5fd1a082 100644 --- a/core/src/main/java/jenkins/security/SlaveToMasterCallable.java +++ b/core/src/main/java/jenkins/security/SlaveToMasterCallable.java @@ -1,14 +1,21 @@ package jenkins.security; import hudson.remoting.Callable; +import org.jenkinsci.remoting.Role; + +import java.util.Collection; /** - * Convenient {@link Callable} with {@link SlaveToMaster} to create anonymous Callable class. + * Convenient {@link Callable} that are meant to run on the master (sent by slave/CLI/etc). * * @author Kohsuke Kawaguchi * @since 1.THU */ -@SlaveToMaster public abstract class SlaveToMasterCallable implements Callable { + @Override + public Collection getRecipients() { + return Roles.FOR_MASTER; + } + private static final long serialVersionUID = 1L; } diff --git a/core/src/main/java/jenkins/slaves/StandardOutputSwapper.java b/core/src/main/java/jenkins/slaves/StandardOutputSwapper.java index f0bb18593d..51b7e2af74 100644 --- a/core/src/main/java/jenkins/slaves/StandardOutputSwapper.java +++ b/core/src/main/java/jenkins/slaves/StandardOutputSwapper.java @@ -4,12 +4,11 @@ import hudson.Extension; import hudson.FilePath; import hudson.model.Computer; import hudson.model.TaskListener; -import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.remoting.StandardOutputStream; import hudson.slaves.ComputerListener; import hudson.util.jna.GNUCLibrary; -import jenkins.security.MasterToSlave; +import jenkins.security.MasterToSlaveCallable; import java.io.File; import java.io.FileDescriptor; @@ -36,8 +35,7 @@ public class StandardOutputSwapper extends ComputerListener { } } - @MasterToSlave - private static final class ChannelSwapper implements Callable { + private static final class ChannelSwapper extends MasterToSlaveCallable { public Boolean call() throws Exception { if (File.pathSeparatorChar==';') return false; // Windows diff --git a/core/src/main/java/jenkins/util/VirtualFile.java b/core/src/main/java/jenkins/util/VirtualFile.java index 5c985ac9c9..bf6017d616 100644 --- a/core/src/main/java/jenkins/util/VirtualFile.java +++ b/core/src/main/java/jenkins/util/VirtualFile.java @@ -39,6 +39,8 @@ import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; +import static hudson.FilePath.MasterToSlaveFileCallable; + /** * Abstraction over {@link File}, {@link FilePath}, or other items such as network resources or ZIP entries. * Assumed to be read-only and makes very limited assumptions, just enough to display content and traverse directories. @@ -334,7 +336,7 @@ public abstract class VirtualFile implements Comparable { } }; } - private static final class Scanner implements FilePath.FileCallable { + private static final class Scanner extends MasterToSlaveFileCallable { private final String glob; Scanner(String glob) { this.glob = glob; @@ -351,7 +353,7 @@ public abstract class VirtualFile implements Comparable { } } - private static final class Readable implements FilePath.FileCallable { + private static final class Readable extends MasterToSlaveFileCallable { @Override public Boolean invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { return f.canRead(); } diff --git a/core/src/test/java/hudson/LauncherTest.java b/core/src/test/java/hudson/LauncherTest.java index 620d95ffb2..13ea826660 100644 --- a/core/src/test/java/hudson/LauncherTest.java +++ b/core/src/test/java/hudson/LauncherTest.java @@ -31,6 +31,8 @@ import hudson.util.ProcessTree; import hudson.util.StreamTaskListener; import hudson.remoting.Callable; import java.io.ByteArrayOutputStream; + +import jenkins.security.MasterToSlaveCallable; import org.apache.commons.io.FileUtils; import org.jvnet.hudson.test.Bug; @@ -74,7 +76,7 @@ public class LauncherTest extends ChannelTestCase { } } - private static final Callable NOOP = new Callable() { + private static final Callable NOOP = new MasterToSlaveCallable() { public Object call() throws RuntimeException { return null; } diff --git a/core/src/test/java/hudson/os/SUTester.java b/core/src/test/java/hudson/os/SUTester.java index 2f28a41303..c89d447c0c 100644 --- a/core/src/test/java/hudson/os/SUTester.java +++ b/core/src/test/java/hudson/os/SUTester.java @@ -2,6 +2,7 @@ package hudson.os; import hudson.remoting.Callable; import hudson.util.StreamTaskListener; +import jenkins.security.MasterToSlaveCallable; import java.io.FileOutputStream; @@ -10,7 +11,7 @@ import java.io.FileOutputStream; */ public class SUTester { public static void main(String[] args) throws Throwable { - SU.execute(StreamTaskListener.fromStdout(),"kohsuke","bogus",new Callable() { + SU.execute(StreamTaskListener.fromStdout(),"kohsuke","bogus",new MasterToSlaveCallable() { public Object call() throws Throwable { System.out.println("Touching /tmp/x"); new FileOutputStream("/tmp/x").close(); diff --git a/core/src/test/java/hudson/util/ProcessTreeTest.java b/core/src/test/java/hudson/util/ProcessTreeTest.java index dd531eac3c..9cea1f228e 100644 --- a/core/src/test/java/hudson/util/ProcessTreeTest.java +++ b/core/src/test/java/hudson/util/ProcessTreeTest.java @@ -5,6 +5,7 @@ import hudson.remoting.Callable; import hudson.remoting.VirtualChannel; import hudson.util.ProcessTree.OSProcess; import hudson.util.ProcessTree.ProcessCallable; +import jenkins.security.MasterToSlaveCallable; import java.io.IOException; import java.io.Serializable; @@ -35,7 +36,7 @@ public class ProcessTreeTest extends ChannelTestCase { t.p.act(new ProcessCallableImpl()); } - private static class MyCallable implements Callable, Serializable { + private static class MyCallable extends MasterToSlaveCallable implements Serializable { public Tag call() throws IOException { Tag t = new Tag(); t.tree = ProcessTree.get(); diff --git a/test/src/test/java/hudson/ProcTest.java b/test/src/test/java/hudson/ProcTest.java index 5eb7752768..6159fc6edd 100644 --- a/test/src/test/java/hudson/ProcTest.java +++ b/test/src/test/java/hudson/ProcTest.java @@ -3,13 +3,12 @@ package hudson; import hudson.Launcher.LocalLauncher; import hudson.Launcher.RemoteLauncher; import hudson.Proc.RemoteProc; -import hudson.remoting.Callable; -import hudson.remoting.Future; import hudson.remoting.Pipe; import hudson.remoting.VirtualChannel; import hudson.slaves.DumbSlave; import hudson.util.IOUtils; import hudson.util.StreamTaskListener; +import jenkins.security.MasterToSlaveCallable; import org.jvnet.hudson.test.Bug; import org.jvnet.hudson.test.HudsonTestCase; @@ -70,7 +69,7 @@ public class ProcTest extends HudsonTestCase { return ch; } - private static class ChannelFiller implements Callable { + private static class ChannelFiller extends MasterToSlaveCallable { private final OutputStream o; private ChannelFiller(OutputStream o) { diff --git a/test/src/test/java/hudson/logging/LogRecorderManagerTest.java b/test/src/test/java/hudson/logging/LogRecorderManagerTest.java index 4bcd2469e7..d27ea875c4 100644 --- a/test/src/test/java/hudson/logging/LogRecorderManagerTest.java +++ b/test/src/test/java/hudson/logging/LogRecorderManagerTest.java @@ -23,6 +23,7 @@ */ package hudson.logging; +import jenkins.security.MasterToSlaveCallable; import org.jvnet.hudson.test.Url; import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlForm; @@ -109,7 +110,7 @@ public class LogRecorderManagerTest { assertFalse(text, text.contains("msg #4")); } - private static final class Log implements Callable { + private static final class Log extends MasterToSlaveCallable { private final Level level; private final String logger; private final String message; diff --git a/test/src/test/java/hudson/slaves/JNLPLauncherTest.java b/test/src/test/java/hudson/slaves/JNLPLauncherTest.java index 8d1e6bf7f4..26e7c1a1a2 100644 --- a/test/src/test/java/hudson/slaves/JNLPLauncherTest.java +++ b/test/src/test/java/hudson/slaves/JNLPLauncherTest.java @@ -35,8 +35,7 @@ import hudson.remoting.Callable; import hudson.remoting.Which; import hudson.util.ArgumentListBuilder; -import jenkins.security.MasterToSlave; -import jenkins.security.SlaveToMaster; +import jenkins.security.SlaveToMasterCallable; import org.jvnet.hudson.test.HudsonTestCase; import org.jvnet.hudson.test.TestExtension; @@ -156,8 +155,7 @@ public class JNLPLauncherTest extends HudsonTestCase { return c; } - @MasterToSlave @SlaveToMaster - private static class NoopTask implements Callable { + private static class NoopTask extends SlaveToMasterCallable { public String call() { return "done"; } diff --git a/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java b/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java index 80c4cef801..3d10d97a26 100644 --- a/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java +++ b/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java @@ -28,6 +28,9 @@ import hudson.FilePath; import hudson.model.Slave; import hudson.remoting.Callable; import java.io.File; +import java.util.Collection; + +import org.jenkinsci.remoting.Role; import org.junit.Test; import static org.junit.Assert.*; import org.junit.Rule; @@ -51,7 +54,7 @@ public class DefaultFilePathFilterTest { // good } assertFalse(reverse.exists()); - System.setProperty(DefaultFilePathFilter.BYPASS_PROP, "true"); + DefaultFilePathFilter.BYPASS = true; s.getChannel().call(new ReverseCallable(reverse)); assertTrue(reverse.exists()); assertEquals("goodbye", reverse.readToString()); @@ -66,6 +69,10 @@ public class DefaultFilePathFilterTest { assertFalse(p.isRemote()); return p.readToString(); } + @Override + public Collection getRecipients() { + return null; // simulate legacy Callable impls + } } private static class ReverseCallable implements Callable { @@ -78,6 +85,9 @@ public class DefaultFilePathFilterTest { p.write("goodbye", null); return null; } + @Override + public Collection getRecipients() { + return null; // simulate legacy Callable impls + } } - } From 91c516200f8d7df05c6f6c25b9504fb843a81a02 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Tue, 23 Sep 2014 19:03:05 -0700 Subject: [PATCH 36/54] Reimplemented the checker as RoleChecker --- .../security/CallableDirectionChecker.java | 109 +++++++++++------- 1 file changed, 69 insertions(+), 40 deletions(-) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index e947ce84c4..50da1e0f79 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -2,35 +2,59 @@ package jenkins.security; import hudson.Extension; import hudson.remoting.Callable; -import hudson.remoting.CallableDecorator; import hudson.remoting.ChannelBuilder; import hudson.slaves.ComputerListener; import hudson.slaves.SlaveComputer; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; - -import java.util.logging.Level; -import java.util.logging.Logger; +import org.jenkinsci.remoting.Role; +import org.jenkinsci.remoting.RoleChecker; +import org.jenkinsci.remoting.RoleSensitive; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.accmod.restrictions.NoExternalUse; +import javax.annotation.Nonnull; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Collection; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.logging.Level; +import java.util.logging.Logger; + /** - * Rejects non-{@link SlaveToMaster} callables. + * Inspects {@link Callable}s that run on the master. * * @author Kohsuke Kawaguchi * @since 1.THU */ @Restricted(NoExternalUse.class) // used implicitly via listener -public class CallableDirectionChecker extends CallableDecorator { +public class CallableDirectionChecker extends RoleChecker { private static final String BYPASS_PROP = CallableDirectionChecker.class.getName()+".allow"; + + /** + * Switch to disable all the defense mechanism completely. + * + * This is an escape hatch in case the fix breaks something critical, to allow the user + * to keep operation. + */ public static boolean BYPASS = Boolean.getBoolean(BYPASS_PROP); - private static final PrintWriter BYPASS_LOG; + + /** + * Whitelist of {@link RoleSensitive} class names (mostly {@link Callable} classes) that are allowed + * to pass through. + */ + public static ConcurrentMap BYPASS_CALLABLES = new ConcurrentHashMap(); + + /** + * If non-null, this object will receive the names of the classes that are dubious. + * Useful in conjunction with {@link #BYPASS} + */ + public static PrintWriter BYPASS_LOG; static { - String log = System.getProperty("jenkins.security.CallableDirectionChecker.log"); + String log = System.getProperty(CallableDirectionChecker.class.getName()+".log"); if (log == null) { BYPASS_LOG = null; } else { @@ -45,39 +69,44 @@ public class CallableDirectionChecker extends CallableDecorator { private CallableDirectionChecker() {} @Override - public Callable userRequest(Callable op, Callable stem) { - Class c = op.getClass(); - String name = c.getName(); - - if (name.startsWith("hudson.remoting")) { // TODO probably insecure - LOGGER.log(Level.FINE, "Sending {0} to master is allowed since it is in remoting", name); - return stem; // lower level services provided by remoting, such IOSyncer, RPCRequest, Ping, etc. that we allow + public void check(RoleSensitive subject, @Nonnull Collection roles) throws SecurityException { + if (Roles.FOR_MASTER.containsAll(roles)) { + LOGGER.log(Level.FINE, "Executing {0} is allowed since it is targeted for the master role"); + return; // known to be safe } - if (c.isAnnotationPresent(SlaveToMaster.class)) { - LOGGER.log(Level.FINE, "Sending {0} is allowed since it is marked @SlaveToMaster", name); - return stem; // known to be safe + final String name = subject.getClass().getName(); + + if (isWhitelisted(name)) { + // this subject is dubious, but we are letting it through as per whitelisting + LOGGER.log(Level.FINE, "Explicitly allowing {0} to be sent from slave to master", name); + + // record those that we are letting through + if (BYPASS_LOG != null) + BYPASS_LOG.println(name); + + return; } - if (BYPASS_LOG != null) { - BYPASS_LOG.println(name); - return stem; + throw new SecurityException("Sending " + name + " from slave to master is prohibited"); + } + + /** + * Is this subject class name whitelisted? + */ + private boolean isWhitelisted(String name) { + if (BYPASS) // everything is whitelisted + return true; + + if (BYPASS_CALLABLES.containsKey(name)) + return true; + + if (Boolean.getBoolean(BYPASS_PROP+"."+name)) { + BYPASS_CALLABLES.put(name,null); + return true; } - if (c.isAnnotationPresent(MasterToSlave.class)) { - throw new SecurityException("Sending " + name + " from slave to master is prohibited"); - } else { - // No annotation provided, so we do not know whether it is safe or not. - if (BYPASS) { - LOGGER.log(Level.FINE, "Allowing {0} to be sent from slave to master", name); - return stem; - } else if (Boolean.getBoolean(BYPASS_PROP + "." + name)) { - LOGGER.log(Level.FINE, "Explicitly allowing {0} to be sent from slave to master", name); - return stem; - } else { - throw new SecurityException("Sending from slave to master is prohibited unless you run with: -D" + BYPASS_PROP + "." + name); - } - } + return false; } /** @@ -88,7 +117,7 @@ public class CallableDirectionChecker extends CallableDecorator { public static class ComputerListenerImpl extends ComputerListener { @Override public void onChannelBuilding(ChannelBuilder builder, SlaveComputer sc) { - builder.with(new CallableDirectionChecker()); + builder.withRoleChecker(new CallableDirectionChecker()); } } From 9a5e8e975df2bc7023e57b0231beae383b12d150 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 24 Sep 2014 07:48:30 -0400 Subject: [PATCH 37/54] BYPASS_LOG is only needed for testing, not regular operation, so it need not be settable at runtime. --- .../java/jenkins/security/CallableDirectionChecker.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index 50da1e0f79..9b6ec361dc 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -48,11 +48,7 @@ public class CallableDirectionChecker extends RoleChecker { */ public static ConcurrentMap BYPASS_CALLABLES = new ConcurrentHashMap(); - /** - * If non-null, this object will receive the names of the classes that are dubious. - * Useful in conjunction with {@link #BYPASS} - */ - public static PrintWriter BYPASS_LOG; + private static final PrintWriter BYPASS_LOG; static { String log = System.getProperty(CallableDirectionChecker.class.getName()+".log"); if (log == null) { From e640fd4dbc85d71b894f538a527b5c720867cef6 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 24 Sep 2014 07:51:31 -0400 Subject: [PATCH 38/54] BYPASS_LOG is meant to _replace_ all logging, and log what _would have been_ violations. (It supersedes other bypass properties.) --- .../jenkins/security/CallableDirectionChecker.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index 9b6ec361dc..836cacecad 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -67,20 +67,22 @@ public class CallableDirectionChecker extends RoleChecker { @Override public void check(RoleSensitive subject, @Nonnull Collection roles) throws SecurityException { if (Roles.FOR_MASTER.containsAll(roles)) { - LOGGER.log(Level.FINE, "Executing {0} is allowed since it is targeted for the master role"); + if (BYPASS_LOG == null) { + LOGGER.log(Level.FINE, "Executing {0} is allowed since it is targeted for the master role"); + } return; // known to be safe } final String name = subject.getClass().getName(); + if (BYPASS_LOG != null) { + BYPASS_LOG.println(name); + return; + } + if (isWhitelisted(name)) { // this subject is dubious, but we are letting it through as per whitelisting LOGGER.log(Level.FINE, "Explicitly allowing {0} to be sent from slave to master", name); - - // record those that we are letting through - if (BYPASS_LOG != null) - BYPASS_LOG.println(name); - return; } From c1116a765e7a42419bdbe0c712b3b03a63df05c4 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 24 Sep 2014 08:14:43 -0400 Subject: [PATCH 39/54] CallableDecorator is newly added, so belongs in the package for new classes. --- core/src/main/java/hudson/slaves/ComputerListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/slaves/ComputerListener.java b/core/src/main/java/hudson/slaves/ComputerListener.java index 173fd0c20f..b2c0fd547c 100644 --- a/core/src/main/java/hudson/slaves/ComputerListener.java +++ b/core/src/main/java/hudson/slaves/ComputerListener.java @@ -31,7 +31,7 @@ import hudson.FilePath; import hudson.model.Computer; import hudson.model.Node; import hudson.model.TaskListener; -import hudson.remoting.CallableDecorator; +import org.jenkinsci.remoting.CallableDecorator; import hudson.remoting.Channel; import hudson.remoting.ChannelBuilder; import jenkins.model.Jenkins; From 42b2f22c252318892205c166710290ea82ce2baf Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 24 Sep 2014 08:36:20 -0400 Subject: [PATCH 40/54] Utility class, no constructor. --- core/src/main/java/jenkins/security/Roles.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/jenkins/security/Roles.java b/core/src/main/java/jenkins/security/Roles.java index e2a65d6efd..3b2c37f207 100644 --- a/core/src/main/java/jenkins/security/Roles.java +++ b/core/src/main/java/jenkins/security/Roles.java @@ -35,4 +35,7 @@ public class Roles { * {@link Role#UNKNOWN}) */ public static final Collection FOR_SLAVE = Collections.singleton(SLAVE); + + private Roles() {} + } From f343595b438e62c47dec2065f26f7cb69bf11e23 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 24 Sep 2014 08:38:19 -0400 Subject: [PATCH 41/54] Make sure new APIs are marked with @since. --- core/src/main/java/jenkins/security/DefaultFilePathFilter.java | 2 -- core/src/main/java/jenkins/security/Roles.java | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/jenkins/security/DefaultFilePathFilter.java b/core/src/main/java/jenkins/security/DefaultFilePathFilter.java index d4032cad61..98c25f0eda 100644 --- a/core/src/main/java/jenkins/security/DefaultFilePathFilter.java +++ b/core/src/main/java/jenkins/security/DefaultFilePathFilter.java @@ -44,8 +44,6 @@ import org.kohsuke.accmod.restrictions.DoNotUse; /** * Blocks slaves from writing to files on the master by default. - * - * @since 1.THU */ @Restricted(DoNotUse.class) // impl @Extension public class DefaultFilePathFilter extends ComputerListener { diff --git a/core/src/main/java/jenkins/security/Roles.java b/core/src/main/java/jenkins/security/Roles.java index 3b2c37f207..b433bca6e8 100644 --- a/core/src/main/java/jenkins/security/Roles.java +++ b/core/src/main/java/jenkins/security/Roles.java @@ -15,6 +15,7 @@ import java.util.Collections; * not have any role. * * @author Kohsuke Kawaguchi + * @since TODO */ public class Roles { public static final Role MASTER = new Role("master"); From 201b035cfccf3c0292faff3492acd008b5f2dace Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 24 Sep 2014 09:29:34 -0700 Subject: [PATCH 42/54] Updated to reflect the signature change around role checking in remoting --- core/src/main/java/hudson/FilePath.java | 20 +++++++++---------- .../security/CallableDirectionChecker.java | 14 ++++++++----- .../security/MasterToSlaveCallable.java | 5 +++-- .../security/SlaveToMasterCallable.java | 5 +++-- .../security/DefaultFilePathFilterTest.java | 9 +++++---- 5 files changed, 29 insertions(+), 24 deletions(-) diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index ed911a4a0a..88c7a1e016 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -67,7 +67,7 @@ import org.apache.tools.ant.types.FileSet; import org.apache.tools.tar.TarEntry; import org.apache.tools.zip.ZipEntry; import org.apache.tools.zip.ZipFile; -import org.jenkinsci.remoting.Role; +import org.jenkinsci.remoting.RoleChecker; import org.jenkinsci.remoting.RoleSensitive; import org.kohsuke.stapler.Stapler; @@ -95,8 +95,6 @@ import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.List; @@ -912,8 +910,8 @@ public final class FilePath implements Serializable { */ public static abstract class MasterToSlaveFileCallable implements FileCallable { @Override - public Collection getRecipients() { - return Roles.FOR_SLAVE; + public void checkRoles(RoleChecker checker) throws SecurityException { + checker.check(this,Roles.SLAVE); } private static final long serialVersionUID = 1L; } @@ -923,8 +921,8 @@ public final class FilePath implements Serializable { */ public static abstract class SlaveToMasterFileCallable implements FileCallable { @Override - public Collection getRecipients() { - return Roles.FOR_MASTER; + public void checkRoles(RoleChecker checker) throws SecurityException { + checker.check(this,Roles.MASTER); } private static final long serialVersionUID = 1L; } @@ -1085,8 +1083,8 @@ public final class FilePath implements Serializable { } @Override - public Collection getRecipients() { - return task.getRecipients(); + public void checkRoles(RoleChecker checker) throws SecurityException { + task.checkRoles(checker); } private static final long serialVersionUID = 1L; @@ -2537,8 +2535,8 @@ public final class FilePath implements Serializable { * Role check comes from {@link FileCallable}s. */ @Override - public Collection getRecipients() { - return callable.getRecipients(); + public void checkRoles(RoleChecker checker) throws SecurityException { + callable.checkRoles(checker); } public ClassLoader getClassLoader() { diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index 836cacecad..dff410cd44 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -48,7 +48,11 @@ public class CallableDirectionChecker extends RoleChecker { */ public static ConcurrentMap BYPASS_CALLABLES = new ConcurrentHashMap(); + /** + * Test feature that is meant to replace all logging, and log what would have been violations. + */ private static final PrintWriter BYPASS_LOG; + static { String log = System.getProperty(CallableDirectionChecker.class.getName()+".log"); if (log == null) { @@ -65,16 +69,16 @@ public class CallableDirectionChecker extends RoleChecker { private CallableDirectionChecker() {} @Override - public void check(RoleSensitive subject, @Nonnull Collection roles) throws SecurityException { - if (Roles.FOR_MASTER.containsAll(roles)) { + public void check(RoleSensitive subject, @Nonnull Collection expected) throws SecurityException { + final String name = subject.getClass().getName(); + + if (expected.contains(Roles.MASTER)) { if (BYPASS_LOG == null) { - LOGGER.log(Level.FINE, "Executing {0} is allowed since it is targeted for the master role"); + LOGGER.log(Level.FINE, "Executing {0} is allowed since it is targeted for the master role", name); } return; // known to be safe } - final String name = subject.getClass().getName(); - if (BYPASS_LOG != null) { BYPASS_LOG.println(name); return; diff --git a/core/src/main/java/jenkins/security/MasterToSlaveCallable.java b/core/src/main/java/jenkins/security/MasterToSlaveCallable.java index 1d498d290e..8be5b98305 100644 --- a/core/src/main/java/jenkins/security/MasterToSlaveCallable.java +++ b/core/src/main/java/jenkins/security/MasterToSlaveCallable.java @@ -2,6 +2,7 @@ package jenkins.security; import hudson.remoting.Callable; import org.jenkinsci.remoting.Role; +import org.jenkinsci.remoting.RoleChecker; import java.util.Collection; @@ -13,8 +14,8 @@ import java.util.Collection; */ public abstract class MasterToSlaveCallable implements Callable { @Override - public Collection getRecipients() { - return Roles.FOR_SLAVE; + public void checkRoles(RoleChecker checker) throws SecurityException { + checker.check(this,Roles.SLAVE); } private static final long serialVersionUID = 1L; diff --git a/core/src/main/java/jenkins/security/SlaveToMasterCallable.java b/core/src/main/java/jenkins/security/SlaveToMasterCallable.java index cb5fd1a082..61265cbdea 100644 --- a/core/src/main/java/jenkins/security/SlaveToMasterCallable.java +++ b/core/src/main/java/jenkins/security/SlaveToMasterCallable.java @@ -2,6 +2,7 @@ package jenkins.security; import hudson.remoting.Callable; import org.jenkinsci.remoting.Role; +import org.jenkinsci.remoting.RoleChecker; import java.util.Collection; @@ -13,8 +14,8 @@ import java.util.Collection; */ public abstract class SlaveToMasterCallable implements Callable { @Override - public Collection getRecipients() { - return Roles.FOR_MASTER; + public void checkRoles(RoleChecker checker) throws SecurityException { + checker.check(this,Roles.MASTER); } private static final long serialVersionUID = 1L; diff --git a/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java b/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java index 3d10d97a26..509bac52fd 100644 --- a/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java +++ b/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java @@ -31,6 +31,7 @@ import java.io.File; import java.util.Collection; import org.jenkinsci.remoting.Role; +import org.jenkinsci.remoting.RoleChecker; import org.junit.Test; import static org.junit.Assert.*; import org.junit.Rule; @@ -70,8 +71,8 @@ public class DefaultFilePathFilterTest { return p.readToString(); } @Override - public Collection getRecipients() { - return null; // simulate legacy Callable impls + public void checkRoles(RoleChecker checker) throws SecurityException { + throw new NoSuchMethodError(); // simulate legacy Callable impls } } @@ -86,8 +87,8 @@ public class DefaultFilePathFilterTest { return null; } @Override - public Collection getRecipients() { - return null; // simulate legacy Callable impls + public void checkRoles(RoleChecker checker) throws SecurityException { + throw new NoSuchMethodError(); // simulate legacy Callable impls } } } From 71fe6e6650816f2ab82b4362375012650ccf3944 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 24 Sep 2014 09:51:31 -0700 Subject: [PATCH 43/54] Signature update complete for RoleChecker change --- .../jenkins/security/NotReallyRoleSensitiveCallable.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/jenkins/security/NotReallyRoleSensitiveCallable.java b/core/src/main/java/jenkins/security/NotReallyRoleSensitiveCallable.java index 5663cd7575..953c5ba69d 100644 --- a/core/src/main/java/jenkins/security/NotReallyRoleSensitiveCallable.java +++ b/core/src/main/java/jenkins/security/NotReallyRoleSensitiveCallable.java @@ -1,9 +1,7 @@ package jenkins.security; import hudson.remoting.Callable; -import org.jenkinsci.remoting.Role; - -import java.util.Collection; +import org.jenkinsci.remoting.RoleChecker; /** * {@link Callable} adapter for situations where Callable is not used for remoting but @@ -14,7 +12,7 @@ import java.util.Collection; */ public abstract class NotReallyRoleSensitiveCallable implements Callable { @Override - public final Collection getRecipients() { + public void checkRoles(RoleChecker checker) throws SecurityException { // not meant to be used where this matters throw new UnsupportedOperationException(); } From e47dc1d5551bee290b31916b291f20c0840e3379 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 24 Sep 2014 10:51:21 -0700 Subject: [PATCH 44/54] I'm begging Jesse to keep those properties modifiable at runtime! It would just provide so much flexibility that would come in handy for operators. - One can control this feature from $JENKINS_HOME/groovy.init.d/*.groovy. When these are managed by Chef/puppet, this make it very clean to apply this as a patch. - The log file is getting too big and wants to rotate? No problem with quick groovy script from the console (or from CLI if you need to do the same with 12 masters) - Shipping the log over to network instead of local file? No problem! I thought your primary concern of field being unreachable from outside is already addressed via @Restricted(NoExternalUse). --- .../main/java/jenkins/security/CallableDirectionChecker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index dff410cd44..6d3a8437d4 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -51,7 +51,7 @@ public class CallableDirectionChecker extends RoleChecker { /** * Test feature that is meant to replace all logging, and log what would have been violations. */ - private static final PrintWriter BYPASS_LOG; + public static PrintWriter BYPASS_LOG; static { String log = System.getProperty(CallableDirectionChecker.class.getName()+".log"); From 1e40aa58055c2b0f03b38d71c94df31a6a375afe Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 24 Sep 2014 11:46:22 -0700 Subject: [PATCH 45/54] Expand the test to cover diagnostic stack trace --- .../java/jenkins/security/DefaultFilePathFilterTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java b/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java index 509bac52fd..0b1a40b680 100644 --- a/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java +++ b/test/src/test/java/jenkins/security/DefaultFilePathFilterTest.java @@ -28,6 +28,8 @@ import hudson.FilePath; import hudson.model.Slave; import hudson.remoting.Callable; import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.Collection; import org.jenkinsci.remoting.Role; @@ -53,6 +55,11 @@ public class DefaultFilePathFilterTest { fail("should have failed"); } catch (SecurityException x) { // good + + // make sure that the stack trace contains the call site info to help assist diagnosis + StringWriter sw = new StringWriter(); + x.printStackTrace(new PrintWriter(sw)); + assertTrue(sw.toString().contains(DefaultFilePathFilterTest.class.getName()+".remotePath")); } assertFalse(reverse.exists()); DefaultFilePathFilter.BYPASS = true; From c5bda70bee226bedbb7049da77f17487d6584869 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 24 Sep 2014 15:49:41 -0400 Subject: [PATCH 46/54] Reverting e47dc1d5551bee290b31916b291f20c0840e3379. The result of a misunderstanding. BYPASS_LOG can be deleted by the time we release, and for its temporary purpose, runtime modification is not necessary. --- .../main/java/jenkins/security/CallableDirectionChecker.java | 3 ++- core/src/main/java/jenkins/security/DefaultFilePathFilter.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index 6d3a8437d4..44e30d2441 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -50,8 +50,9 @@ public class CallableDirectionChecker extends RoleChecker { /** * Test feature that is meant to replace all logging, and log what would have been violations. + * TODO delete before release */ - public static PrintWriter BYPASS_LOG; + private static final PrintWriter BYPASS_LOG; static { String log = System.getProperty(CallableDirectionChecker.class.getName()+".log"); diff --git a/core/src/main/java/jenkins/security/DefaultFilePathFilter.java b/core/src/main/java/jenkins/security/DefaultFilePathFilter.java index 98c25f0eda..ec6f4bb9e2 100644 --- a/core/src/main/java/jenkins/security/DefaultFilePathFilter.java +++ b/core/src/main/java/jenkins/security/DefaultFilePathFilter.java @@ -52,7 +52,7 @@ import org.kohsuke.accmod.restrictions.DoNotUse; * Escape hatch to disable this check completely. */ public static boolean BYPASS = Boolean.getBoolean(DefaultFilePathFilter.class.getName()+".allow"); - private static final PrintWriter BYPASS_LOG; + private static final PrintWriter BYPASS_LOG; // TODO delete before release static { String log = System.getProperty("jenkins.security.DefaultFilePathFilter.log"); if (log == null) { From c3a2069603bad6ba4f3c89c22e4c15242195b023 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 24 Sep 2014 13:53:22 -0700 Subject: [PATCH 47/54] Added a 'ChannelConfigurator' extension point ... to intercept new Channel creation, which provides the opportunity to set up the new channel. --- .../src/main/java/hudson/slaves/Channels.java | 114 +++++++++++------- .../java/hudson/slaves/ComputerListener.java | 10 -- .../java/hudson/slaves/SlaveComputer.java | 5 +- .../src/main/java/jenkins/FilePathFilter.java | 9 +- .../security/CallableDirectionChecker.java | 6 +- .../jenkins/security/ChannelConfigurator.java | 44 +++++++ .../security/DefaultFilePathFilter.java | 9 +- 7 files changed, 127 insertions(+), 70 deletions(-) create mode 100644 core/src/main/java/jenkins/security/ChannelConfigurator.java diff --git a/core/src/main/java/hudson/slaves/Channels.java b/core/src/main/java/hudson/slaves/Channels.java index 6f43934203..347a0be924 100644 --- a/core/src/main/java/hudson/slaves/Channels.java +++ b/core/src/main/java/hudson/slaves/Channels.java @@ -29,15 +29,19 @@ import hudson.FilePath; import hudson.model.Computer; import hudson.model.TaskListener; import hudson.remoting.Channel; +import hudson.remoting.ChannelBuilder; +import hudson.remoting.CommandTransport; import hudson.remoting.Launcher; import hudson.remoting.SocketInputStream; import hudson.remoting.SocketOutputStream; import hudson.util.ClasspathBuilder; import hudson.util.JVMBuilder; import hudson.util.StreamCopyThread; +import jenkins.security.ChannelConfigurator; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; +import java.io.IOError; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -68,66 +72,84 @@ public class Channels { * we kill the process. */ public static Channel forProcess(String name, ExecutorService execService, InputStream in, OutputStream out, OutputStream header, final Proc proc) throws IOException { - return new Channel(name, execService, in, out, header) { - /** - * Kill the process when the channel is severed. - */ + ChannelBuilder cb = new ChannelBuilder(name,execService) { @Override - public synchronized void terminate(IOException e) { - super.terminate(e); - try { - proc.kill(); - } catch (IOException x) { - // we are already in the error recovery mode, so just record it and move on - LOGGER.log(Level.INFO, "Failed to terminate the severed connection",x); - } catch (InterruptedException x) { - // process the interrupt later - Thread.currentThread().interrupt(); - } - } + public Channel build(CommandTransport transport) throws IOException { + return new Channel(this,transport) { + /** + * Kill the process when the channel is severed. + */ + @Override + public synchronized void terminate(IOException e) { + super.terminate(e); + try { + proc.kill(); + } catch (IOException x) { + // we are already in the error recovery mode, so just record it and move on + LOGGER.log(Level.INFO, "Failed to terminate the severed connection",x); + } catch (InterruptedException x) { + // process the interrupt later + Thread.currentThread().interrupt(); + } + } - @Override - public synchronized void close() throws IOException { - super.close(); - // wait for the child process to complete - try { - proc.join(); - } catch (InterruptedException e) { - // process the interrupt later - Thread.currentThread().interrupt(); - } + @Override + public synchronized void join() throws InterruptedException { + super.join(); + // wait for the child process to complete, too + try { + proc.join(); + } catch (IOException e) { + throw new IOError(e); + } + } + }; } }; + cb.withHeaderStream(header); + + for (ChannelConfigurator cc : ChannelConfigurator.all()) { + cc.onChannelBuilding(cb,null); // TODO: what to pass as a context? + } + + return cb.build(in,out); } public static Channel forProcess(String name, ExecutorService execService, final Process proc, OutputStream header) throws IOException { final Thread thread = new StreamCopyThread(name + " stderr", proc.getErrorStream(), header); thread.start(); - return new Channel(name, execService, proc.getInputStream(), proc.getOutputStream(), header) { - /** - * Kill the process when the channel is severed. - */ + ChannelBuilder cb = new ChannelBuilder(name,execService) { @Override - public synchronized void terminate(IOException e) { - super.terminate(e); - proc.destroy(); - // the stderr copier should exit by itself - } + public Channel build(CommandTransport transport) throws IOException { + return new Channel(this,transport) { + /** + * Kill the process when the channel is severed. + */ + @Override + public synchronized void terminate(IOException e) { + super.terminate(e); + proc.destroy(); + // the stderr copier should exit by itself + } - @Override - public synchronized void close() throws IOException { - super.close(); - // wait for Maven to complete - try { - proc.waitFor(); - thread.join(); - } catch (InterruptedException e) { - // process the interrupt later - Thread.currentThread().interrupt(); - } + @Override + public synchronized void join() throws InterruptedException { + super.join(); + // wait for the child process to complete, too + proc.waitFor(); + thread.join(); + } + }; } }; + cb.withHeaderStream(header); + + for (ChannelConfigurator cc : ChannelConfigurator.all()) { + cc.onChannelBuilding(cb,null); // TODO: what to pass as a context? + } + + return cb.build(proc.getInputStream(),proc.getOutputStream()); } /** diff --git a/core/src/main/java/hudson/slaves/ComputerListener.java b/core/src/main/java/hudson/slaves/ComputerListener.java index b2c0fd547c..eea4b6cb93 100644 --- a/core/src/main/java/hudson/slaves/ComputerListener.java +++ b/core/src/main/java/hudson/slaves/ComputerListener.java @@ -46,16 +46,6 @@ import java.io.IOException; */ public abstract class ComputerListener implements ExtensionPoint { - /** - * Called before {@link Channel} is constructed to provide opportunities to affect the way the channel is run. - * - *

    - * Among other things, this is useful to add {@link CallableDecorator}s. - * - * @since 1.THU - */ - public void onChannelBuilding(ChannelBuilder builder, SlaveComputer sc) {} - /** * Called before a {@link ComputerLauncher} is asked to launch a connection with {@link Computer}. * diff --git a/core/src/main/java/hudson/slaves/SlaveComputer.java b/core/src/main/java/hudson/slaves/SlaveComputer.java index 1b1517bd46..b66e4358f2 100644 --- a/core/src/main/java/hudson/slaves/SlaveComputer.java +++ b/core/src/main/java/hudson/slaves/SlaveComputer.java @@ -72,6 +72,7 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import javax.servlet.RequestDispatcher; import jenkins.model.Jenkins; +import jenkins.security.ChannelConfigurator; import jenkins.security.MasterToSlaveCallable; import jenkins.slaves.JnlpSlaveAgentProtocol; import org.kohsuke.stapler.StaplerRequest; @@ -348,8 +349,8 @@ public class SlaveComputer extends Computer { .withMode(Channel.Mode.NEGOTIATE) .withHeaderStream(launchLog); - for (ComputerListener cl : ComputerListener.all()) { - cl.onChannelBuilding(cb,this); + for (ChannelConfigurator cc : ChannelConfigurator.all()) { + cc.onChannelBuilding(cb,this); } Channel channel = cb.build(in,out); diff --git a/core/src/main/java/jenkins/FilePathFilter.java b/core/src/main/java/jenkins/FilePathFilter.java index 1c98933cc5..32dd7a3396 100644 --- a/core/src/main/java/jenkins/FilePathFilter.java +++ b/core/src/main/java/jenkins/FilePathFilter.java @@ -2,6 +2,7 @@ package jenkins; import hudson.FilePath; import hudson.remoting.Channel; +import hudson.remoting.ChannelBuilder; import javax.annotation.CheckForNull; import java.io.File; @@ -57,12 +58,12 @@ public abstract class FilePathFilter { public void stat(File f) throws SecurityException {} - public final void installTo(Channel ch) { - synchronized (ch) { - FilePathFilterAggregator filters = ch.getProperty(FilePathFilterAggregator.KEY); + public final void installTo(ChannelBuilder cb) { + synchronized (cb) { + FilePathFilterAggregator filters = (FilePathFilterAggregator) cb.getProperties().get(FilePathFilterAggregator.KEY); if (filters==null) { filters = new FilePathFilterAggregator(); - ch.setProperty(FilePathFilterAggregator.KEY,filters); + cb.withProperty(FilePathFilterAggregator.KEY,filters); } filters.add(this); } diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index 44e30d2441..1496b6bed6 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -3,8 +3,6 @@ package jenkins.security; import hudson.Extension; import hudson.remoting.Callable; import hudson.remoting.ChannelBuilder; -import hudson.slaves.ComputerListener; -import hudson.slaves.SlaveComputer; import org.jenkinsci.remoting.Role; import org.jenkinsci.remoting.RoleChecker; import org.jenkinsci.remoting.RoleSensitive; @@ -117,9 +115,9 @@ public class CallableDirectionChecker extends RoleChecker { */ @Restricted(DoNotUse.class) // impl @Extension - public static class ComputerListenerImpl extends ComputerListener { + public static class ChannelConfiguratorImpl extends ChannelConfigurator { @Override - public void onChannelBuilding(ChannelBuilder builder, SlaveComputer sc) { + public void onChannelBuilding(ChannelBuilder builder, Object context) { builder.withRoleChecker(new CallableDirectionChecker()); } } diff --git a/core/src/main/java/jenkins/security/ChannelConfigurator.java b/core/src/main/java/jenkins/security/ChannelConfigurator.java new file mode 100644 index 0000000000..a371ae5401 --- /dev/null +++ b/core/src/main/java/jenkins/security/ChannelConfigurator.java @@ -0,0 +1,44 @@ +package jenkins.security; + +import hudson.ExtensionList; +import hudson.ExtensionPoint; +import hudson.remoting.Channel; +import hudson.remoting.ChannelBuilder; +import hudson.slaves.SlaveComputer; +import jenkins.model.Jenkins; + +import javax.annotation.Nullable; + +/** + * Intercepts the new creation of {@link Channel} and tweak its configuration. + * + * @author Kohsuke Kawaguchi + * @since 1.THU + */ +public abstract class ChannelConfigurator implements ExtensionPoint { + /** + * Called whenever a new channel is being built. + * + * @param builder + * Configures the newly built channel. The callee + * can call its methods to modify its settings. + * @param context + * The parameter that helps the callee determines what this channel is for. + * Legacy callers do not always provide this information, in which case this value might be null. + * + * Possible known values include: + * + *

    + *
    {@link SlaveComputer} + *
    When a channel is being established to talk to a slave. + *
    + */ + public void onChannelBuilding(ChannelBuilder builder, @Nullable Object context) {} + + /** + * All the registered {@link ChannelConfigurator}s. + */ + public static ExtensionList all() { + return Jenkins.getInstance().getExtensionList(ChannelConfigurator.class); + } +} diff --git a/core/src/main/java/jenkins/security/DefaultFilePathFilter.java b/core/src/main/java/jenkins/security/DefaultFilePathFilter.java index ec6f4bb9e2..e6c2ddd7da 100644 --- a/core/src/main/java/jenkins/security/DefaultFilePathFilter.java +++ b/core/src/main/java/jenkins/security/DefaultFilePathFilter.java @@ -29,6 +29,7 @@ import hudson.FilePath; import hudson.model.Computer; import hudson.model.TaskListener; import hudson.remoting.Channel; +import hudson.remoting.ChannelBuilder; import hudson.slaves.ComputerListener; import java.io.File; import java.io.FileNotFoundException; @@ -46,7 +47,7 @@ import org.kohsuke.accmod.restrictions.DoNotUse; * Blocks slaves from writing to files on the master by default. */ @Restricted(DoNotUse.class) // impl -@Extension public class DefaultFilePathFilter extends ComputerListener { +@Extension public class DefaultFilePathFilter extends ChannelConfigurator { /** * Escape hatch to disable this check completely. @@ -67,7 +68,8 @@ import org.kohsuke.accmod.restrictions.DoNotUse; } private static final Logger LOGGER = Logger.getLogger(DefaultFilePathFilter.class.getName()); - @Override public void preOnline(Computer c, Channel channel, FilePath root, TaskListener listener) throws IOException, InterruptedException { + @Override + public void onChannelBuilding(ChannelBuilder builder, Object context) { new FilePathFilter() { private void op(String op, File f) throws SecurityException { if (BYPASS_LOG != null) { @@ -99,7 +101,6 @@ import org.kohsuke.accmod.restrictions.DoNotUse; @Override public void stat(File f) throws SecurityException { op("stat", f); } - }.installTo(channel); + }.installTo(builder); } - } From 1af88e8b7f64bca28d86b036e0fd59555d3cb4e3 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 24 Sep 2014 14:03:21 -0700 Subject: [PATCH 48/54] Disable classloading from slaves and other VMs connected to the master --- .../main/java/jenkins/security/CallableDirectionChecker.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index 1496b6bed6..9e17dc86f8 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -118,6 +118,11 @@ public class CallableDirectionChecker extends RoleChecker { public static class ChannelConfiguratorImpl extends ChannelConfigurator { @Override public void onChannelBuilding(ChannelBuilder builder, Object context) { + // if the big red emergency button is pressed, then we need to disable the defense mechanism, + // including enabling classloading. + if (BYPASS) return; + + builder.withRemoteClassLoadingAllowed(false); builder.withRoleChecker(new CallableDirectionChecker()); } } From 4f733244e62e0cd3d9943c004927e98c51d949d0 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 24 Sep 2014 17:06:27 -0700 Subject: [PATCH 49/54] Moving MasterToSlaveFileCallable/SlaveToMasterFileCallable to the top level classes. If we ever decide to develop a compatibility thunk library for these changes to help promote deployment in plugins, then these classes being a nested type is problematic as they'll fail to load under onld Jenkins --- core/src/main/java/hudson/FilePath.java | 25 ++----------------- .../DiskSpaceMonitorDescriptor.java | 2 +- .../node_monitors/TemporarySpaceMonitor.java | 2 +- .../hudson/os/solaris/ZFSProvisioner.java | 2 +- .../java/hudson/tasks/ArtifactArchiver.java | 2 +- .../main/java/hudson/tasks/Fingerprinter.java | 3 +-- core/src/main/java/hudson/tasks/Maven.java | 4 +-- .../java/hudson/tasks/junit/JUnitParser.java | 2 +- .../test/DefaultTestResultParserImpl.java | 2 +- .../hudson/tools/ZipExtractionInstaller.java | 2 +- .../jenkins/MasterToSlaveFileCallable.java | 18 +++++++++++++ .../jenkins/SlaveToMasterFileCallable.java | 18 +++++++++++++ .../main/java/jenkins/util/VirtualFile.java | 2 +- 13 files changed, 48 insertions(+), 36 deletions(-) create mode 100644 core/src/main/java/jenkins/MasterToSlaveFileCallable.java create mode 100644 core/src/main/java/jenkins/SlaveToMasterFileCallable.java diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 88c7a1e016..7b1caea5dc 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -54,9 +54,10 @@ import hudson.util.IOUtils; import hudson.util.io.Archiver; import hudson.util.io.ArchiverFactory; import jenkins.FilePathFilter; +import jenkins.MasterToSlaveFileCallable; +import jenkins.SlaveToMasterFileCallable; import jenkins.model.Jenkins; import jenkins.security.MasterToSlaveCallable; -import jenkins.security.Roles; import jenkins.util.VirtualFile; import org.apache.commons.fileupload.FileItem; import org.apache.commons.io.input.CountingInputStream; @@ -905,28 +906,6 @@ public final class FilePath implements Serializable { T invoke(File f, VirtualChannel channel) throws IOException, InterruptedException; } - /** - * {@link FileCallable}s that are meant to be only used on the master. - */ - public static abstract class MasterToSlaveFileCallable implements FileCallable { - @Override - public void checkRoles(RoleChecker checker) throws SecurityException { - checker.check(this,Roles.SLAVE); - } - private static final long serialVersionUID = 1L; - } - - /** - * {@link FileCallable}s that can be executed on the master, sent by the slave. - */ - public static abstract class SlaveToMasterFileCallable implements FileCallable { - @Override - public void checkRoles(RoleChecker checker) throws SecurityException { - checker.check(this,Roles.MASTER); - } - private static final long serialVersionUID = 1L; - } - /** * {@link FileCallable}s that can be executed anywhere, including the master. * diff --git a/core/src/main/java/hudson/node_monitors/DiskSpaceMonitorDescriptor.java b/core/src/main/java/hudson/node_monitors/DiskSpaceMonitorDescriptor.java index 4d53f720b5..45bef339ba 100644 --- a/core/src/main/java/hudson/node_monitors/DiskSpaceMonitorDescriptor.java +++ b/core/src/main/java/hudson/node_monitors/DiskSpaceMonitorDescriptor.java @@ -23,7 +23,7 @@ */ package hudson.node_monitors; -import hudson.FilePath.MasterToSlaveFileCallable; +import jenkins.MasterToSlaveFileCallable; import hudson.remoting.VirtualChannel; import hudson.Util; import hudson.slaves.OfflineCause; diff --git a/core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java b/core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java index 707c043c5f..03e892f130 100644 --- a/core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java +++ b/core/src/main/java/hudson/node_monitors/TemporarySpaceMonitor.java @@ -25,7 +25,7 @@ package hudson.node_monitors; import hudson.Extension; import hudson.FilePath; -import hudson.FilePath.MasterToSlaveFileCallable; +import jenkins.MasterToSlaveFileCallable; import hudson.Functions; import hudson.model.Computer; import hudson.remoting.Callable; diff --git a/core/src/main/java/hudson/os/solaris/ZFSProvisioner.java b/core/src/main/java/hudson/os/solaris/ZFSProvisioner.java index b2f5a4443a..649a4880f2 100644 --- a/core/src/main/java/hudson/os/solaris/ZFSProvisioner.java +++ b/core/src/main/java/hudson/os/solaris/ZFSProvisioner.java @@ -23,7 +23,7 @@ */ package hudson.os.solaris; -import hudson.FilePath.MasterToSlaveFileCallable; +import jenkins.MasterToSlaveFileCallable; import hudson.FileSystemProvisioner; import hudson.FilePath; import hudson.WorkspaceSnapshot; diff --git a/core/src/main/java/hudson/tasks/ArtifactArchiver.java b/core/src/main/java/hudson/tasks/ArtifactArchiver.java index a78aae14b2..ff971902f6 100644 --- a/core/src/main/java/hudson/tasks/ArtifactArchiver.java +++ b/core/src/main/java/hudson/tasks/ArtifactArchiver.java @@ -24,7 +24,7 @@ package hudson.tasks; import hudson.FilePath; -import hudson.FilePath.MasterToSlaveFileCallable; +import jenkins.MasterToSlaveFileCallable; import hudson.Launcher; import hudson.Util; import hudson.Extension; diff --git a/core/src/main/java/hudson/tasks/Fingerprinter.java b/core/src/main/java/hudson/tasks/Fingerprinter.java index a5a378b3f3..fdcff26028 100644 --- a/core/src/main/java/hudson/tasks/Fingerprinter.java +++ b/core/src/main/java/hudson/tasks/Fingerprinter.java @@ -27,8 +27,7 @@ import com.google.common.collect.ImmutableMap; import hudson.EnvVars; import hudson.Extension; import hudson.FilePath; -import hudson.FilePath.FileCallable; -import hudson.FilePath.MasterToSlaveFileCallable; +import jenkins.MasterToSlaveFileCallable; import hudson.Launcher; import hudson.Util; import hudson.matrix.MatrixConfiguration; diff --git a/core/src/main/java/hudson/tasks/Maven.java b/core/src/main/java/hudson/tasks/Maven.java index 95c3f6f61e..2b35075e16 100644 --- a/core/src/main/java/hudson/tasks/Maven.java +++ b/core/src/main/java/hudson/tasks/Maven.java @@ -24,14 +24,13 @@ package hudson.tasks; import hudson.Extension; -import hudson.FilePath.MasterToSlaveFileCallable; +import jenkins.MasterToSlaveFileCallable; import hudson.Launcher; import hudson.Functions; import hudson.EnvVars; import hudson.Util; import hudson.CopyOnWrite; import hudson.Launcher.LocalLauncher; -import hudson.FilePath.FileCallable; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.BuildListener; @@ -43,7 +42,6 @@ import jenkins.mvn.GlobalMavenConfig; import jenkins.mvn.GlobalSettingsProvider; import jenkins.mvn.SettingsProvider; import hudson.model.TaskListener; -import hudson.remoting.Callable; import hudson.remoting.VirtualChannel; import hudson.slaves.NodeSpecific; import hudson.tasks._maven.MavenConsoleAnnotator; diff --git a/core/src/main/java/hudson/tasks/junit/JUnitParser.java b/core/src/main/java/hudson/tasks/junit/JUnitParser.java index d72944263d..fa001dc130 100644 --- a/core/src/main/java/hudson/tasks/junit/JUnitParser.java +++ b/core/src/main/java/hudson/tasks/junit/JUnitParser.java @@ -23,7 +23,7 @@ */ package hudson.tasks.junit; -import hudson.FilePath.MasterToSlaveFileCallable; +import jenkins.MasterToSlaveFileCallable; import hudson.model.TaskListener; import hudson.tasks.test.TestResultParser; import hudson.model.AbstractBuild; diff --git a/core/src/main/java/hudson/tasks/test/DefaultTestResultParserImpl.java b/core/src/main/java/hudson/tasks/test/DefaultTestResultParserImpl.java index ab364c0566..d4a23172cc 100644 --- a/core/src/main/java/hudson/tasks/test/DefaultTestResultParserImpl.java +++ b/core/src/main/java/hudson/tasks/test/DefaultTestResultParserImpl.java @@ -25,7 +25,7 @@ package hudson.tasks.test; import hudson.AbortException; import hudson.FilePath; -import hudson.FilePath.MasterToSlaveFileCallable; +import jenkins.MasterToSlaveFileCallable; import hudson.Launcher; import hudson.Util; import hudson.model.AbstractBuild; diff --git a/core/src/main/java/hudson/tools/ZipExtractionInstaller.java b/core/src/main/java/hudson/tools/ZipExtractionInstaller.java index ff3b3837ec..e92064624d 100644 --- a/core/src/main/java/hudson/tools/ZipExtractionInstaller.java +++ b/core/src/main/java/hudson/tools/ZipExtractionInstaller.java @@ -26,7 +26,7 @@ package hudson.tools; import hudson.Extension; import hudson.FilePath; -import hudson.FilePath.MasterToSlaveFileCallable; +import jenkins.MasterToSlaveFileCallable; import hudson.ProxyConfiguration; import hudson.Util; import hudson.Functions; diff --git a/core/src/main/java/jenkins/MasterToSlaveFileCallable.java b/core/src/main/java/jenkins/MasterToSlaveFileCallable.java new file mode 100644 index 0000000000..cad6813c18 --- /dev/null +++ b/core/src/main/java/jenkins/MasterToSlaveFileCallable.java @@ -0,0 +1,18 @@ +package jenkins; + +import hudson.FilePath.FileCallable; +import jenkins.security.Roles; +import org.jenkinsci.remoting.RoleChecker; + +/** + * {@link FileCallable}s that are meant to be only used on the master. + * + * @since 1.THU + */ +public abstract class MasterToSlaveFileCallable implements FileCallable { + @Override + public void checkRoles(RoleChecker checker) throws SecurityException { + checker.check(this, Roles.SLAVE); + } + private static final long serialVersionUID = 1L; +} diff --git a/core/src/main/java/jenkins/SlaveToMasterFileCallable.java b/core/src/main/java/jenkins/SlaveToMasterFileCallable.java new file mode 100644 index 0000000000..f196a88cf3 --- /dev/null +++ b/core/src/main/java/jenkins/SlaveToMasterFileCallable.java @@ -0,0 +1,18 @@ +package jenkins; + +import hudson.FilePath.FileCallable; +import jenkins.security.Roles; +import org.jenkinsci.remoting.RoleChecker; + +/** + * {@link FileCallable}s that can be executed on the master, sent by the slave. + * + * @since 1.THU + */ +public abstract class SlaveToMasterFileCallable implements FileCallable { + @Override + public void checkRoles(RoleChecker checker) throws SecurityException { + checker.check(this, Roles.MASTER); + } + private static final long serialVersionUID = 1L; +} diff --git a/core/src/main/java/jenkins/util/VirtualFile.java b/core/src/main/java/jenkins/util/VirtualFile.java index bf6017d616..6441959ed5 100644 --- a/core/src/main/java/jenkins/util/VirtualFile.java +++ b/core/src/main/java/jenkins/util/VirtualFile.java @@ -39,7 +39,7 @@ import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; -import static hudson.FilePath.MasterToSlaveFileCallable; +import jenkins.MasterToSlaveFileCallable; /** * Abstraction over {@link File}, {@link FilePath}, or other items such as network resources or ZIP entries. From 40c4a2f35698b88b965cb1b10ded86d38c2ed3f3 Mon Sep 17 00:00:00 2001 From: Kohsuke Kawaguchi Date: Wed, 24 Sep 2014 17:14:41 -0700 Subject: [PATCH 50/54] Got rid of the unused constants --- core/src/main/java/jenkins/security/Roles.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/jenkins/security/Roles.java b/core/src/main/java/jenkins/security/Roles.java index b433bca6e8..cca26873a5 100644 --- a/core/src/main/java/jenkins/security/Roles.java +++ b/core/src/main/java/jenkins/security/Roles.java @@ -2,9 +2,6 @@ package jenkins.security; import org.jenkinsci.remoting.Role; -import java.util.Collection; -import java.util.Collections; - /** * Predefined {@link Role}s in Jenkins. * @@ -15,17 +12,13 @@ import java.util.Collections; * not have any role. * * @author Kohsuke Kawaguchi - * @since TODO + * @since 1.THU */ public class Roles { - public static final Role MASTER = new Role("master"); - /** * Indicates that a callable runs on masters, requested by slaves/CLI/maven/whatever. */ - public static final Collection FOR_MASTER = Collections.singleton(MASTER); - - public static final Role SLAVE = new Role("slave"); + public static final Role MASTER = new Role("master"); /** * Indicates that a callable is meant to run on slaves. @@ -35,8 +28,7 @@ public class Roles { * the master (as opposed to ones that do not have that information, which gets * {@link Role#UNKNOWN}) */ - public static final Collection FOR_SLAVE = Collections.singleton(SLAVE); + public static final Role SLAVE = new Role("slave"); private Roles() {} - } From 695a66f1962eb23606a80480d8bef2376dab09e7 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 25 Sep 2014 09:25:15 -0400 Subject: [PATCH 51/54] 1af88e8b7f64bca28d86b036e0fd59555d3cb4e3 incorrectly disabled all logging. When we are allowing all callables through, we want to enable remote class loading (just in case someone is using it legitimately), but continue logging callable issues. Currently there is no remoting flag to allow remote class loading while logging it. --- .../java/jenkins/security/CallableDirectionChecker.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/jenkins/security/CallableDirectionChecker.java b/core/src/main/java/jenkins/security/CallableDirectionChecker.java index 9e17dc86f8..e9698a1cc9 100644 --- a/core/src/main/java/jenkins/security/CallableDirectionChecker.java +++ b/core/src/main/java/jenkins/security/CallableDirectionChecker.java @@ -120,9 +120,10 @@ public class CallableDirectionChecker extends RoleChecker { public void onChannelBuilding(ChannelBuilder builder, Object context) { // if the big red emergency button is pressed, then we need to disable the defense mechanism, // including enabling classloading. - if (BYPASS) return; - - builder.withRemoteClassLoadingAllowed(false); + if (!BYPASS && BYPASS_LOG == null) { + builder.withRemoteClassLoadingAllowed(false); + } + // In either of the above cases, the check method will return normally, but may log things. builder.withRoleChecker(new CallableDirectionChecker()); } } From 801b82578ed7897250207c3edce1ea0a386db6c8 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 25 Sep 2014 09:52:48 -0400 Subject: [PATCH 52/54] Bundling patched Maven plugin. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 45dc95616d..81874be25e 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ THE SOFTWARE. jenkins-jira 1.7.4 - 2.0 + 2.0.5-SNAPSHOT ${skipTests} 6 From 5612b235efd719e028e2ba365fe0b6a8e8eb0f91 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 4 Oct 2013 12:50:28 -0400 Subject: [PATCH 53/54] Moving hudson.maven tests to maven-plugin. (cherry picked from commit facfc178dfecb17decfc757a186d2888a79ea684) --- test/pom.xml | 6 - .../hudson/maven/AbortedMavenBuildTest.java | 35 - .../maven/AbstractMaven3xBuildTest.java | 263 -------- .../java/hudson/maven/Maven30xBuildTest.java | 55 -- .../java/hudson/maven/Maven31xBuildTest.java | 54 -- .../maven/MavenArgumentInterceptorTest.java | 171 ----- .../maven/MavenBuildSurefireFailedTest.java | 85 --- .../java/hudson/maven/MavenBuildTest.java | 204 ------ .../hudson/maven/MavenEmptyModuleTest.java | 38 -- .../java/hudson/maven/MavenJellyTest.java | 40 -- .../java/hudson/maven/MavenModuleSetTest.java | 32 - ...ltiModuleLogRotatorCleanArtifactsTest.java | 109 --- .../hudson/maven/MavenMultiModuleTest.java | 622 ------------------ .../MavenMultiModuleTestIncremental.java | 70 -- .../test/java/hudson/maven/MavenOptsTest.java | 127 ---- .../java/hudson/maven/MavenProjectTest.java | 242 ------- .../maven/MavenSnapshotTriggerTest.java | 75 --- .../hudson/maven/ModuleDependencyLocator.java | 83 --- .../maven/PlexusModuleContributorTest.java | 73 -- .../hudson/maven/RedeployPublisherTest.java | 256 ------- .../hudson/maven/ResultExposingBuilder.java | 52 -- .../maven/reporters/MavenMailerTest.java | 96 --- .../reporters/MavenSiteArchiverTest.java | 16 - .../maven/reporters/SurefireArchiverTest.java | 62 -- .../resources/hudson/maven/HUDSON-4192.zip | Bin 2117 -> 0 bytes .../resources/hudson/maven/JENKINS-1557.zip | Bin 3420 -> 0 bytes .../resources/hudson/maven/JENKINS-15865.zip | Bin 2086 -> 0 bytes .../resources/hudson/maven/JENKINS-8573.zip | Bin 3739 -> 0 bytes .../resources/hudson/maven/JENKINS-9326.zip | Bin 12849 -> 0 bytes .../resources/hudson/maven/big-artifact.pom | 67 -- .../hudson/maven/custom-plexus-component.pom | 34 - .../hudson/maven/envars-maven-project.zip | Bin 3374 -> 0 bytes .../hudson/maven/foobar_eclipse_with_fix.zip | Bin 10089 -> 0 bytes .../maven/incorrect-inheritence-testcase.zip | Bin 1004 -> 0 bytes .../maven/maven-compilation-failure.zip | Bin 2160 -> 0 bytes .../hudson/maven/maven-dep-test-A-changed.zip | Bin 2597 -> 0 bytes .../hudson/maven/maven-dep-test-A.zip | Bin 2593 -> 0 bytes .../hudson/maven/maven-dep-test-B.zip | Bin 2606 -> 0 bytes .../hudson/maven/maven-dep-test-C.zip | Bin 2610 -> 0 bytes .../hudson/maven/maven-empty-mod.zip | Bin 1465 -> 0 bytes .../hudson/maven/maven-multimod-changes.zip | Bin 1063 -> 0 bytes .../hudson/maven/maven-multimod-incr.zip | Bin 10295 -> 0 bytes .../hudson/maven/maven-multimod-rel-base.zip | Bin 8620 -> 0 bytes .../resources/hudson/maven/maven-multimod.zip | Bin 8707 -> 0 bytes .../hudson/maven/maven-multimodule-site.zip | Bin 1678 -> 0 bytes .../maven/maven-multimodule-unit-failure.zip | Bin 7771 -> 0 bytes .../hudson/maven/maven-opts-echo.zip | Bin 623 -> 0 bytes .../hudson/maven/maven-surefire-unstable.zip | Bin 2459 -> 0 bytes .../maven/maven-test-failure-findbugs.zip | Bin 2289 -> 0 bytes .../resources/hudson/maven/maven3-project.zip | Bin 12960 -> 0 bytes .../hudson/maven/multimodule-maven.zip | Bin 5639 -> 0 bytes .../maven/several-modules-in-directory.zip | Bin 870 -> 0 bytes .../resources/hudson/maven/targz-artifact.pom | 54 -- .../resources/hudson/maven/test-pom-7162.xml | 26 - .../resources/hudson/maven/test-pom-8395.xml | 25 - 55 files changed, 3072 deletions(-) delete mode 100644 test/src/test/java/hudson/maven/AbortedMavenBuildTest.java delete mode 100755 test/src/test/java/hudson/maven/AbstractMaven3xBuildTest.java delete mode 100755 test/src/test/java/hudson/maven/Maven30xBuildTest.java delete mode 100755 test/src/test/java/hudson/maven/Maven31xBuildTest.java delete mode 100644 test/src/test/java/hudson/maven/MavenArgumentInterceptorTest.java delete mode 100755 test/src/test/java/hudson/maven/MavenBuildSurefireFailedTest.java delete mode 100755 test/src/test/java/hudson/maven/MavenBuildTest.java delete mode 100644 test/src/test/java/hudson/maven/MavenEmptyModuleTest.java delete mode 100644 test/src/test/java/hudson/maven/MavenJellyTest.java delete mode 100644 test/src/test/java/hudson/maven/MavenModuleSetTest.java delete mode 100644 test/src/test/java/hudson/maven/MavenMultiModuleLogRotatorCleanArtifactsTest.java delete mode 100644 test/src/test/java/hudson/maven/MavenMultiModuleTest.java delete mode 100755 test/src/test/java/hudson/maven/MavenMultiModuleTestIncremental.java delete mode 100644 test/src/test/java/hudson/maven/MavenOptsTest.java delete mode 100644 test/src/test/java/hudson/maven/MavenProjectTest.java delete mode 100644 test/src/test/java/hudson/maven/MavenSnapshotTriggerTest.java delete mode 100644 test/src/test/java/hudson/maven/ModuleDependencyLocator.java delete mode 100644 test/src/test/java/hudson/maven/PlexusModuleContributorTest.java delete mode 100644 test/src/test/java/hudson/maven/RedeployPublisherTest.java delete mode 100644 test/src/test/java/hudson/maven/ResultExposingBuilder.java delete mode 100644 test/src/test/java/hudson/maven/reporters/MavenMailerTest.java delete mode 100644 test/src/test/java/hudson/maven/reporters/MavenSiteArchiverTest.java delete mode 100644 test/src/test/java/hudson/maven/reporters/SurefireArchiverTest.java delete mode 100644 test/src/test/resources/hudson/maven/HUDSON-4192.zip delete mode 100644 test/src/test/resources/hudson/maven/JENKINS-1557.zip delete mode 100644 test/src/test/resources/hudson/maven/JENKINS-15865.zip delete mode 100644 test/src/test/resources/hudson/maven/JENKINS-8573.zip delete mode 100644 test/src/test/resources/hudson/maven/JENKINS-9326.zip delete mode 100644 test/src/test/resources/hudson/maven/big-artifact.pom delete mode 100644 test/src/test/resources/hudson/maven/custom-plexus-component.pom delete mode 100644 test/src/test/resources/hudson/maven/envars-maven-project.zip delete mode 100644 test/src/test/resources/hudson/maven/foobar_eclipse_with_fix.zip delete mode 100755 test/src/test/resources/hudson/maven/incorrect-inheritence-testcase.zip delete mode 100644 test/src/test/resources/hudson/maven/maven-compilation-failure.zip delete mode 100644 test/src/test/resources/hudson/maven/maven-dep-test-A-changed.zip delete mode 100755 test/src/test/resources/hudson/maven/maven-dep-test-A.zip delete mode 100755 test/src/test/resources/hudson/maven/maven-dep-test-B.zip delete mode 100755 test/src/test/resources/hudson/maven/maven-dep-test-C.zip delete mode 100644 test/src/test/resources/hudson/maven/maven-empty-mod.zip delete mode 100644 test/src/test/resources/hudson/maven/maven-multimod-changes.zip delete mode 100644 test/src/test/resources/hudson/maven/maven-multimod-incr.zip delete mode 100644 test/src/test/resources/hudson/maven/maven-multimod-rel-base.zip delete mode 100644 test/src/test/resources/hudson/maven/maven-multimod.zip delete mode 100644 test/src/test/resources/hudson/maven/maven-multimodule-site.zip delete mode 100755 test/src/test/resources/hudson/maven/maven-multimodule-unit-failure.zip delete mode 100644 test/src/test/resources/hudson/maven/maven-opts-echo.zip delete mode 100644 test/src/test/resources/hudson/maven/maven-surefire-unstable.zip delete mode 100755 test/src/test/resources/hudson/maven/maven-test-failure-findbugs.zip delete mode 100644 test/src/test/resources/hudson/maven/maven3-project.zip delete mode 100644 test/src/test/resources/hudson/maven/multimodule-maven.zip delete mode 100644 test/src/test/resources/hudson/maven/several-modules-in-directory.zip delete mode 100644 test/src/test/resources/hudson/maven/targz-artifact.pom delete mode 100644 test/src/test/resources/hudson/maven/test-pom-7162.xml delete mode 100755 test/src/test/resources/hudson/maven/test-pom-8395.xml diff --git a/test/pom.xml b/test/pom.xml index f476795c21..371fee6e03 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -161,12 +161,6 @@ THE SOFTWARE. geb-implicit-assertions 0.7.2 - - org.jenkins-ci.test - sample-plexus-component - 1.0 - test - diff --git a/test/src/test/java/hudson/maven/AbortedMavenBuildTest.java b/test/src/test/java/hudson/maven/AbortedMavenBuildTest.java deleted file mode 100644 index 9275040c8c..0000000000 --- a/test/src/test/java/hudson/maven/AbortedMavenBuildTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package hudson.maven; - -import hudson.Launcher; -import hudson.model.BuildListener; -import hudson.model.Result; -import org.jvnet.hudson.test.Bug; -import org.jvnet.hudson.test.ExtractResourceSCM; -import org.jvnet.hudson.test.HudsonTestCase; - -import java.io.IOException; - -public class AbortedMavenBuildTest extends HudsonTestCase { - @Bug(8054) - public void testBuildWrapperSeesAbortedStatus() throws Exception { - configureDefaultMaven(); - MavenModuleSet project = createMavenProject(); - TestBuildWrapper wrapper = new TestBuildWrapper(); - project.getBuildWrappersList().add(wrapper); - project.getReporters().add(new AbortingReporter()); - project.setGoals("clean"); - project.setScm(new ExtractResourceSCM(getClass().getResource("maven-empty-mod.zip"))); - MavenModuleSetBuild build = project.scheduleBuild2(0).get(); - assertEquals(Result.ABORTED, build.getResult()); - assertEquals(Result.ABORTED, wrapper.buildResultInTearDown); - } - - private static class AbortingReporter extends MavenReporter { - private static final long serialVersionUID = 1L; - - @Override - public boolean end(MavenBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { - throw new InterruptedException(); - } - } -} diff --git a/test/src/test/java/hudson/maven/AbstractMaven3xBuildTest.java b/test/src/test/java/hudson/maven/AbstractMaven3xBuildTest.java deleted file mode 100755 index b69a03e573..0000000000 --- a/test/src/test/java/hudson/maven/AbstractMaven3xBuildTest.java +++ /dev/null @@ -1,263 +0,0 @@ -package hudson.maven; - -/* - * Olivier Lamy - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import hudson.Launcher; -import hudson.model.BuildListener; -import hudson.model.ParametersDefinitionProperty; -import hudson.model.Result; -import hudson.model.StringParameterDefinition; -import hudson.tasks.Maven.MavenInstallation; -import hudson.tasks.test.AbstractTestResultAction; -import hudson.tasks.test.TestResultProjectAction; -import org.apache.commons.io.FileUtils; -import org.jvnet.hudson.test.Bug; -import org.jvnet.hudson.test.Email; -import org.jvnet.hudson.test.ExtractResourceSCM; -import org.jvnet.hudson.test.HudsonTestCase; - -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; - -/** - * @author Olivier Lamy - */ -public abstract class AbstractMaven3xBuildTest - extends HudsonTestCase { - - public abstract MavenInstallation configureMaven3x() throws Exception; - - public void testSimpleMaven3Build() throws Exception { - - MavenModuleSet m = createMavenProject(); - MavenInstallation mavenInstallation = configureMaven3x(); - m.setMaven( mavenInstallation.getName() ); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven3-project.zip"))); - m.setGoals( "clean install" ); - MavenModuleSetBuild b = buildAndAssertSuccess(m); - assertTrue( MavenUtil.maven3orLater( b.getMavenVersionUsed() ) ); - } - - public void testSimpleMaven3BuildRedeployPublisher() throws Exception { - - MavenModuleSet m = createMavenProject(); - MavenInstallation mavenInstallation = configureMaven3x(); - m.setMaven( mavenInstallation.getName() ); - File repo = createTmpDir(); - FileUtils.cleanDirectory( repo ); - m.getReporters().add(new TestReporter()); - m.getPublishersList().add(new RedeployPublisher("",repo.toURI().toString(),true, false)); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven3-project.zip"))); - m.setGoals( "clean install" ); - MavenModuleSetBuild b = buildAndAssertSuccess(m); - assertTrue( MavenUtil.maven3orLater( b.getMavenVersionUsed() ) ); - File artifactDir = new File(repo,"com/mycompany/app/my-app/1.7-SNAPSHOT/"); - String[] files = artifactDir.list( new FilenameFilter() - { - - public boolean accept( File dir, String name ) - { - System.out.println("file name : " +name ); - return name.endsWith( ".jar" ); - } - }); - assertTrue("SNAPSHOT exist",!files[0].contains( "SNAPSHOT" )); - assertTrue("file not ended with -1.jar", files[0].endsWith( "-1.jar" )); - } - - public void testSiteBuildWithForkedMojo() throws Exception { - MavenModuleSet m = createMavenProject(); - MavenInstallation mavenInstallation = configureMaven3x(); - m.setMaven( mavenInstallation.getName() ); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven3-project.zip"))); - m.setGoals( "clean site" ); - MavenModuleSetBuild b = buildAndAssertSuccess(m); - assertTrue( MavenUtil.maven3orLater( b.getMavenVersionUsed() ) ); - } - - @Bug(value=8395) - public void testMaven3BuildWrongScope() throws Exception { - - File pom = new File(this.getClass().getResource("test-pom-8395.xml").toURI()); - MavenModuleSet m = createMavenProject(); - MavenInstallation mavenInstallation = configureMaven3x(); - m.setMaven( mavenInstallation.getName() ); - m.getReporters().add(new TestReporter()); - m.setRootPOM(pom.getAbsolutePath()); - m.setGoals( "clean validate" ); - MavenModuleSetBuild mmsb = m.scheduleBuild2( 0 ).get(); - assertBuildStatus( Result.FAILURE, mmsb ); - System.out.println("mmsb.getProject().getModules " + mmsb.getProject().getModules() ); - assertTrue( mmsb.getProject().getModules().isEmpty()); - } - - @Bug(value=8390) - public void testMaven3BuildWrongInheritence() throws Exception { - - MavenModuleSet m = createMavenProject(); - MavenInstallation mavenInstallation = configureMaven3x(); - m.setMaven( mavenInstallation.getName() ); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("incorrect-inheritence-testcase.zip"))); - m.setGoals( "clean validate" ); - MavenModuleSetBuild mmsb = m.scheduleBuild2( 0 ).get(); - assertBuildStatus( Result.FAILURE, mmsb ); - System.out.println("mmsb.getProject().getModules " + mmsb.getProject().getModules() ); - assertTrue( mmsb.getProject().getModules().isEmpty()); - } - - @Bug(value=8445) - public void testMavenSeveralModulesInDirectory() throws Exception { - - MavenModuleSet m = createMavenProject(); - MavenInstallation mavenInstallation = configureMaven3x(); - m.setMaven( mavenInstallation.getName() ); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("several-modules-in-directory.zip"))); - m.setGoals( "clean validate" ); - MavenModuleSetBuild mmsb = buildAndAssertSuccess(m); - assertFalse( mmsb.getProject().getModules().isEmpty()); - } - - @Email("https://groups.google.com/d/msg/hudson-users/Xhw00UopVN0/FA9YqDAIsSYJ") - public void testMavenWithDependencyVersionInEnvVar() throws Exception { - - MavenModuleSet m = createMavenProject(); - MavenInstallation mavenInstallation = configureMaven3x(); - ParametersDefinitionProperty parametersDefinitionProperty = - new ParametersDefinitionProperty(new StringParameterDefinition( "JUNITVERSION", "3.8.2" )); - - m.addProperty( parametersDefinitionProperty ); - m.setMaven( mavenInstallation.getName() ); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("envars-maven-project.zip"))); - m.setGoals( "clean test-compile" ); - MavenModuleSetBuild mmsb = buildAndAssertSuccess(m); - assertFalse( mmsb.getProject().getModules().isEmpty()); - } - - @Bug(8484) - public void testMultiModMavenNonRecursive() throws Exception { - MavenInstallation mavenInstallation = configureMaven3x(); - MavenModuleSet m = createMavenProject(); - m.setMaven( mavenInstallation.getName() ); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-multimod.zip"))); - m.setGoals( "-N validate" ); - assertTrue("MavenModuleSet.isNonRecursive() should be true", m.isNonRecursive()); - buildAndAssertSuccess(m); - assertEquals("not only one module", 1, m.getModules().size()); - } - - @Bug(8573) - public void testBuildTimeStampProperty() throws Exception { - MavenInstallation mavenInstallation = configureMaven3x(); - MavenModuleSet m = createMavenProject(); - m.setMaven( mavenInstallation.getName() ); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("JENKINS-8573.zip"))); - m.setGoals( "process-resources" ); - buildAndAssertSuccess(m); - String content = m.getLastBuild().getWorkspace().child( "target/classes/test.txt" ).readToString(); - assertFalse( content.contains( "${maven.build.timestamp}") ); - assertFalse( content.contains( "${maven.build.timestamp}") ); - } - - @Bug(1557) - public void testDuplicateTestResults() throws Exception { - MavenInstallation mavenInstallation = configureMaven3x(); - MavenModuleSet m = createMavenProject(); - m.setMaven( mavenInstallation.getName() ); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("JENKINS-1557.zip"))); - m.setGoals("verify"); - buildAndAssertSuccess(m); - - int totalCount = m.getModules().iterator().next() - .getAction(TestResultProjectAction.class).getLastTestResultAction().getTotalCount(); - assertEquals(4, totalCount); - } - - @Bug(9326) - public void testTychoTestResults() throws Exception { - MavenInstallation mavenInstallation = configureMaven3x(); - MavenModuleSet m = createMavenProject(); - m.setRootPOM( "org.foobar.build/pom.xml" ); - m.setMaven( mavenInstallation.getName() ); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("JENKINS-9326.zip"),"foobar")); - m.setGoals("verify"); - buildAndAssertSuccess(m); - - System.out.println("modules size " + m.getModules()); - - - MavenModule testModule = null; - for (MavenModule mavenModule : m.getModules()) { - System.out.println("module " + mavenModule.getName() + "/" + mavenModule.getDisplayName()); - if ("org.foobar:org.foobar.test".equals( mavenModule.getName() )) testModule = mavenModule; - } - - AbstractTestResultAction trpa = testModule.getLastBuild().getTestResultAction(); - - int totalCount = trpa.getTotalCount(); - assertEquals(1, totalCount); - } - - @Bug(9326) - public void testTychoEclipseTestResults() throws Exception { - MavenInstallation mavenInstallation = configureMaven3x(); - MavenModuleSet m = createMavenProject(); - m.setRootPOM( "org.foobar.build/pom.xml" ); - m.setMaven( mavenInstallation.getName() ); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("foobar_eclipse_with_fix.zip"),"foobar_eclipse")); - m.setGoals("verify"); - buildAndAssertSuccess(m); - - System.out.println("modules size " + m.getModules()); - - - MavenModule testModule = null; - for (MavenModule mavenModule : m.getModules()) { - System.out.println("module " + mavenModule.getName() + "/" + mavenModule.getDisplayName()); - if ("org.foobar:org.foobar.test".equals( mavenModule.getName() )) testModule = mavenModule; - } - - AbstractTestResultAction trpa = testModule.getLastBuild().getTestResultAction(); - - int totalCount = trpa.getTotalCount(); - assertEquals(1, totalCount); - } - - private static class TestReporter extends MavenReporter { - @Override - public boolean end(MavenBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { - assertNotNull(build.getProject().getWorkspace()); - assertNotNull(build.getWorkspace()); - return true; - } - } - -} diff --git a/test/src/test/java/hudson/maven/Maven30xBuildTest.java b/test/src/test/java/hudson/maven/Maven30xBuildTest.java deleted file mode 100755 index 5c236ce046..0000000000 --- a/test/src/test/java/hudson/maven/Maven30xBuildTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package hudson.maven; - -/* - * Olivier Lamy - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import hudson.Launcher; -import hudson.model.BuildListener; -import hudson.model.ParametersDefinitionProperty; -import hudson.model.Result; -import hudson.model.StringParameterDefinition; -import hudson.tasks.Maven; -import hudson.tasks.Maven.MavenInstallation; -import hudson.tasks.test.AbstractTestResultAction; -import hudson.tasks.test.TestResultProjectAction; -import org.apache.commons.io.FileUtils; -import org.jvnet.hudson.test.Bug; -import org.jvnet.hudson.test.Email; -import org.jvnet.hudson.test.ExtractResourceSCM; -import org.jvnet.hudson.test.HudsonTestCase; - -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; - -/** - * @author Olivier Lamy - */ -public class Maven30xBuildTest - extends AbstractMaven3xBuildTest { - - @Override - public MavenInstallation configureMaven3x() - throws Exception - { - return configureMaven3(); - } - -} diff --git a/test/src/test/java/hudson/maven/Maven31xBuildTest.java b/test/src/test/java/hudson/maven/Maven31xBuildTest.java deleted file mode 100755 index 449d782492..0000000000 --- a/test/src/test/java/hudson/maven/Maven31xBuildTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package hudson.maven; - -/* - * Olivier Lamy - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import hudson.Launcher; -import hudson.model.BuildListener; -import hudson.model.ParametersDefinitionProperty; -import hudson.model.Result; -import hudson.model.StringParameterDefinition; -import hudson.tasks.Maven; -import hudson.tasks.Maven.MavenInstallation; -import hudson.tasks.test.AbstractTestResultAction; -import hudson.tasks.test.TestResultProjectAction; -import org.apache.commons.io.FileUtils; -import org.jvnet.hudson.test.Bug; -import org.jvnet.hudson.test.Email; -import org.jvnet.hudson.test.ExtractResourceSCM; -import org.jvnet.hudson.test.HudsonTestCase; - -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; - -/** - * @author Olivier Lamy - */ -public class Maven31xBuildTest - extends AbstractMaven3xBuildTest { - - @Override - public MavenInstallation configureMaven3x() - throws Exception - { - return configureMaven31(); - } -} diff --git a/test/src/test/java/hudson/maven/MavenArgumentInterceptorTest.java b/test/src/test/java/hudson/maven/MavenArgumentInterceptorTest.java deleted file mode 100644 index 6709fd36b6..0000000000 --- a/test/src/test/java/hudson/maven/MavenArgumentInterceptorTest.java +++ /dev/null @@ -1,171 +0,0 @@ -package hudson.maven; - -/* - * The MIT License - * - * Copyright (c) 2011, Dominik Bartholdi - * - * 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. - */ -import hudson.Extension; -import hudson.Launcher; -import hudson.model.AbstractBuild; -import hudson.model.AbstractProject; -import hudson.model.BuildListener; -import hudson.model.Result; -import hudson.tasks.BuildWrapper; -import hudson.tasks.BuildWrapperDescriptor; -import hudson.tasks.Maven.MavenInstallation; -import hudson.util.ArgumentListBuilder; - -import java.io.IOException; -import java.util.Arrays; -import java.util.List; - -import net.sf.json.JSONObject; - -import org.jvnet.hudson.test.ExtractResourceSCM; -import org.jvnet.hudson.test.HudsonTestCase; -import org.kohsuke.stapler.StaplerRequest; - -/** - * @author Dominik Bartholdi (imod) - */ -public class MavenArgumentInterceptorTest extends HudsonTestCase { - - public void testSimpleMaven3BuildWithArgInterceptor_Goals() throws Exception { - - MavenModuleSet m = createMavenProject(); - MavenInstallation mavenInstallation = configureMaven3(); - m.setMaven(mavenInstallation.getName()); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven3-project.zip"))); - m.setGoals("dummygoal"); // build would fail with this goal - - // add an action to build, redefining the goals and options to be - // executed - m.getBuildWrappersList().add(new TestMvnBuildWrapper("clean")); - - MavenModuleSetBuild b = buildAndAssertSuccess(m); - assertTrue(MavenUtil.maven3orLater(b.getMavenVersionUsed())); - } - - public void testSimpleMaven3BuildWithArgInterceptor_ArgBuilder() throws Exception { - - MavenModuleSet m = createMavenProject(); - MavenInstallation mavenInstallation = configureMaven3(); - m.setMaven(mavenInstallation.getName()); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-multimodule-unit-failure.zip"))); - m.setGoals("clean install"); // build would fail because of failing unit - // tests - - // add an action to build, adding argument to skip the test execution - m.getBuildWrappersList().add(new TestMvnBuildWrapper(Arrays.asList("-DskipTests"))); - - MavenModuleSetBuild b = buildAndAssertSuccess(m); - assertTrue(MavenUtil.maven3orLater(b.getMavenVersionUsed())); - } - - private static class TestMvnArgInterceptor implements MavenArgumentInterceptorAction { - private String goalsAndOptions; - private List args; - - public TestMvnArgInterceptor(String goalsAndOptions) { - this.goalsAndOptions = goalsAndOptions; - } - - public TestMvnArgInterceptor(List args) { - this.args = args; - } - - public String getIconFileName() { - return null; - } - - public String getDisplayName() { - return null; - } - - public String getUrlName() { - return null; - } - - public String getGoalsAndOptions(MavenModuleSetBuild build) { - return goalsAndOptions; - } - - public ArgumentListBuilder intercept(ArgumentListBuilder mavenargs, MavenModuleSetBuild build) { - if (args != null) { - for (String arg : this.args) { - mavenargs.add(arg); - } - } - return mavenargs; - } - } - - public static class TestMvnBuildWrapper extends BuildWrapper { - public Result buildResultInTearDown; - private String goalsAndOptions; - private List args; - - public TestMvnBuildWrapper(String goalsAndOptions) { - this.goalsAndOptions = goalsAndOptions; - } - - public TestMvnBuildWrapper(List args) { - this.args = args; - } - - @Override - public Environment setUp(AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException { - - if (goalsAndOptions != null) { - build.addAction(new TestMvnArgInterceptor(goalsAndOptions)); - } else if (args != null) { - build.addAction(new TestMvnArgInterceptor(args)); - } - - return new BuildWrapper.Environment() { - @Override - public boolean tearDown(AbstractBuild build, BuildListener listener) throws IOException, InterruptedException { - buildResultInTearDown = build.getResult(); - return true; - } - }; - } - - @Extension - public static class TestMvnBuildWrapperDescriptor extends BuildWrapperDescriptor { - @Override - public boolean isApplicable(AbstractProject project) { - return true; - } - - @Override - public BuildWrapper newInstance(StaplerRequest req, JSONObject formData) { - throw new UnsupportedOperationException(); - } - - @Override - public String getDisplayName() { - return this.getClass().getName(); - } - } - } -} diff --git a/test/src/test/java/hudson/maven/MavenBuildSurefireFailedTest.java b/test/src/test/java/hudson/maven/MavenBuildSurefireFailedTest.java deleted file mode 100755 index 1bcece674b..0000000000 --- a/test/src/test/java/hudson/maven/MavenBuildSurefireFailedTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package hudson.maven; - -import hudson.model.Result; -import hudson.tasks.Shell; -import java.util.concurrent.Callable; - -import org.jvnet.hudson.test.Bug; -import org.jvnet.hudson.test.ExtractResourceSCM; -import org.jvnet.hudson.test.HudsonTestCase; -import org.jvnet.hudson.test.RunLoadCounter; - -/** - * @author Olivier Lamy - */ -public class MavenBuildSurefireFailedTest extends HudsonTestCase { - - @Bug(8415) - public void testMaven2Unstable() throws Exception { - configureDefaultMaven(); - MavenModuleSet m = createMavenProject(); - m.setGoals( "test" ); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-multimodule-unit-failure.zip"))); - assertBuildStatus(Result.UNSTABLE, m.scheduleBuild2(0).get()); - } - - @Bug(8415) - public void testMaven2Failed() throws Exception { - configureDefaultMaven(); - final MavenModuleSet m = createMavenProject(); - m.setGoals( "test -Dmaven.test.failure.ignore=false" ); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-multimodule-unit-failure.zip"))); - assertBuildStatus(Result.FAILURE, m.scheduleBuild2(0).get()); - // JENKINS-18895: - MavenModule failing = m.getModule("com.mycompany.app:my-app"); - assertEquals(Result.FAILURE, failing.getLastBuild().getResult()); - RunLoadCounter.prepare(failing); - assertEquals(Result.FAILURE, RunLoadCounter.assertMaxLoads(failing, 0, new Callable() { - @Override public Result call() throws Exception { - return m.getLastBuild().getResult(); - } - })); - } - - @Bug(8415) - public void testMaven3Unstable() throws Exception { - MavenModuleSet m = createMavenProject(); - m.setMaven( configureMaven3().getName() ); - m.setGoals( "test" ); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-multimodule-unit-failure.zip"))); - assertBuildStatus(Result.UNSTABLE, m.scheduleBuild2(0).get()); - } - - @Bug(8415) - public void testMaven3Failed() throws Exception { - MavenModuleSet m = createMavenProject(); - m.setMaven( configureMaven3().getName() ); - m.setGoals( "test -Dmaven.test.failure.ignore=false" ); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-multimodule-unit-failure.zip"))); - assertBuildStatus(Result.FAILURE, m.scheduleBuild2(0).get()); - } - - @Bug(14102) - public void testMaven3SkipPostBuilder() throws Exception { - MavenModuleSet m = createMavenProject(); - m.setMaven( configureMaven3().getName() ); - m.setGoals( "test" ); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-multimodule-unit-failure.zip"))); - // run dummy command only if build state is SUCCESS - m.setRunPostStepsIfResult(Result.SUCCESS); - m.addPostBuilder(new Shell("no-valid-command")); - assertBuildStatus(Result.UNSTABLE, m.scheduleBuild2(0).get()); - } - - @Bug(14102) - public void testMaven2SkipPostBuilder() throws Exception { - configureDefaultMaven(); - MavenModuleSet m = createMavenProject(); - m.setGoals( "test" ); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-multimodule-unit-failure.zip"))); - // run dummy command only if build state is SUCCESS - m.setRunPostStepsIfResult(Result.SUCCESS); - m.addPostBuilder(new Shell("no-valid-command")); - assertBuildStatus(Result.UNSTABLE, m.scheduleBuild2(0).get()); - } -} diff --git a/test/src/test/java/hudson/maven/MavenBuildTest.java b/test/src/test/java/hudson/maven/MavenBuildTest.java deleted file mode 100755 index 9dd02c337e..0000000000 --- a/test/src/test/java/hudson/maven/MavenBuildTest.java +++ /dev/null @@ -1,204 +0,0 @@ -package hudson.maven; - -import hudson.Launcher; -import hudson.model.BuildListener; -import hudson.model.ParametersDefinitionProperty; -import hudson.model.Result; -import hudson.model.StringParameterDefinition; -import hudson.tasks.Maven.MavenInstallation; -import hudson.tasks.test.AbstractTestResultAction; -import hudson.tasks.test.AggregatedTestResultAction; - -import java.io.File; -import java.io.IOException; -import java.util.Map; - -import org.jvnet.hudson.test.Bug; -import org.jvnet.hudson.test.Email; -import org.jvnet.hudson.test.ExtractResourceSCM; -import org.jvnet.hudson.test.HudsonTestCase; -import org.jvnet.hudson.test.SingleFileSCM; - -/** - * @author Kohsuke Kawaguchi - */ -public class MavenBuildTest extends HudsonTestCase { - - /** - * NPE in {@code build.getProject().getWorkspace()} for {@link MavenBuild}. - */ - @Bug(4192) - public void testMavenWorkspaceExists() throws Exception { - configureDefaultMaven(); - MavenModuleSet m = createMavenProject(); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("HUDSON-4192.zip"))); - buildAndAssertSuccess(m); - } - - /** - * {@link Result} getting set to SUCCESS even if there's a test failure, when the test failure - * does not happen in the final task segment. - */ - @Bug(4177) - public void testTestFailureInEarlyTaskSegment() throws Exception { - configureDefaultMaven(); - MavenModuleSet m = createMavenProject(); - m.setGoals("clean install findbugs:findbugs"); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-test-failure-findbugs.zip"))); - assertBuildStatus(Result.UNSTABLE, m.scheduleBuild2(0).get()); - } - - /** - * Verify that a compilation error properly shows up as a failure. - */ - public void testCompilationFailure() throws Exception { - configureDefaultMaven(); - MavenModuleSet m = createMavenProject(); - m.setGoals("clean install"); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-compilation-failure.zip"))); - assertBuildStatus(Result.FAILURE, m.scheduleBuild2(0).get()); - } - - /** - * Workspace determination problem on non-aggregator style build. - */ - @Bug(4226) - public void testParallelModuleBuild() throws Exception { - configureDefaultMaven(); - MavenModuleSet m = createMavenProject(); - m.setScm(new ExtractResourceSCM(getClass().getResource("multimodule-maven.zip"))); - - buildAndAssertSuccess(m); - - m.setAggregatorStyleBuild(false); - - // run module builds - buildAndAssertSuccess(m.getModule("test$module1")); - buildAndAssertSuccess(m.getModule("test$module1")); - } - - @Bug(value=8395) - public void testMaven2BuildWrongScope() throws Exception { - - File pom = new File(this.getClass().getResource("test-pom-8395.xml").toURI()); - MavenModuleSet m = createMavenProject(); - MavenInstallation mavenInstallation = configureDefaultMaven(); - m.setMaven( mavenInstallation.getName() ); - m.getReporters().add(new TestReporter()); - m.setRootPOM(pom.getAbsolutePath()); - m.setGoals( "clean validate" ); - MavenModuleSetBuild mmsb = buildAndAssertSuccess(m); - assertFalse( mmsb.getProject().getModules().isEmpty()); - } - - @Bug(value=8390) - public void testMaven2BuildWrongInheritence() throws Exception { - - MavenModuleSet m = createMavenProject(); - MavenInstallation mavenInstallation = configureDefaultMaven(); - m.setMaven( mavenInstallation.getName() ); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("incorrect-inheritence-testcase.zip"))); - m.setGoals( "clean validate" ); - MavenModuleSetBuild mmsb = buildAndAssertSuccess(m); - assertFalse( mmsb.getProject().getModules().isEmpty()); - } - - @Bug(value=8445) - public void testMaven2SeveralModulesInDirectory() throws Exception { - - MavenModuleSet m = createMavenProject(); - MavenInstallation mavenInstallation = configureDefaultMaven(); - m.setMaven( mavenInstallation.getName() ); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("several-modules-in-directory.zip"))); - m.setGoals( "clean validate" ); - MavenModuleSetBuild mmsb = buildAndAssertSuccess(m); - assertFalse( mmsb.getProject().getModules().isEmpty()); - } - - @Email("https://groups.google.com/d/msg/hudson-users/Xhw00UopVN0/FA9YqDAIsSYJ") - public void testMavenWithDependencyVersionInEnvVar() throws Exception { - - MavenModuleSet m = createMavenProject(); - MavenInstallation mavenInstallation = configureDefaultMaven(); - ParametersDefinitionProperty parametersDefinitionProperty = - new ParametersDefinitionProperty(new StringParameterDefinition( "JUNITVERSION", "3.8.2" )); - - m.addProperty( parametersDefinitionProperty ); - m.setMaven( mavenInstallation.getName() ); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("envars-maven-project.zip"))); - m.setGoals( "clean test-compile" ); - MavenModuleSetBuild mmsb = buildAndAssertSuccess(m); - assertFalse( mmsb.getProject().getModules().isEmpty()); - } - - @Bug(8573) - public void testBuildTimeStampProperty() throws Exception { - MavenInstallation mavenInstallation = configureDefaultMaven(); - MavenModuleSet m = createMavenProject(); - m.setMaven( mavenInstallation.getName() ); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("JENKINS-8573.zip"))); - m.setGoals( "process-resources" ); - buildAndAssertSuccess(m); - String content = m.getLastBuild().getWorkspace().child( "target/classes/test.txt" ).readToString(); - assertFalse( content.contains( "${maven.build.timestamp}") ); - assertFalse( content.contains( "${maven.build.timestamp}") ); - - System.out.println( "content " + content ); - } - - @Bug(value=15865) - public void testMavenFailsafePluginTestResultsAreRecorded() throws Exception { - - // GIVEN: a Maven project with maven-failsafe-plugin and Maven 2.2.1 - MavenModuleSet mavenProject = createMavenProject(); - MavenInstallation mavenInstallation = configureDefaultMaven(); - mavenProject.setMaven(mavenInstallation.getName()); - mavenProject.getReporters().add(new TestReporter()); - mavenProject.setScm(new ExtractResourceSCM(getClass().getResource("JENKINS-15865.zip"))); - mavenProject.setGoals( "clean install" ); - - // WHEN project is build - MavenModuleSetBuild mmsb = buildAndAssertSuccess(mavenProject); - - // THEN we have a testresult recorded - AggregatedTestResultAction aggregatedTestResultAction = mmsb.getAggregatedTestResultAction(); - assertNotNull(aggregatedTestResultAction); - assertEquals(1, aggregatedTestResultAction.getTotalCount()); - - Map moduleBuilds = mmsb.getModuleLastBuilds(); - assertEquals(1, moduleBuilds.size()); - MavenBuild moduleBuild = moduleBuilds.values().iterator().next(); - AbstractTestResultAction testResultAction = moduleBuild.getTestResultAction(); - assertNotNull(testResultAction); - assertEquals(1, testResultAction.getTotalCount()); - } - - @Bug(18178) - public void testExtensionsConflictingWithCore() throws Exception { - MavenModuleSet m = createMavenProject(); - m.setMaven(configureDefaultMaven().getName()); - m.setScm(new SingleFileSCM("pom.xml", - "4.0.0" + - "ga0" + - "" + - "org.springframework.build.awsorg.springframework.build.aws.maven3.0.0.RELEASE" + - "")); - buildAndAssertSuccess(m); - } - - private static class TestReporter extends MavenReporter { - private static final long serialVersionUID = 1L; - - @Override - public boolean end(MavenBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { - assertNotNull(build.getWorkspace()); - return true; - } - } - -} diff --git a/test/src/test/java/hudson/maven/MavenEmptyModuleTest.java b/test/src/test/java/hudson/maven/MavenEmptyModuleTest.java deleted file mode 100644 index b8ad72048a..0000000000 --- a/test/src/test/java/hudson/maven/MavenEmptyModuleTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package hudson.maven; - -import org.jvnet.hudson.test.HudsonTestCase; -import org.jvnet.hudson.test.Bug; -import org.jvnet.hudson.test.ExtractResourceSCM; - - -import hudson.Launcher; -import hudson.model.BuildListener; - -import java.io.IOException; - -/** - * @author Andrew Bayer - */ -public class MavenEmptyModuleTest extends HudsonTestCase { - /** - * Verify that a build will work with a module and a module - */ - @Bug(4442) - public void testEmptyModuleParsesAndBuilds() throws Exception { - configureDefaultMaven(); - MavenModuleSet m = createMavenProject(); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-empty-mod.zip"))); - buildAndAssertSuccess(m); - } - - private static class TestReporter extends MavenReporter { - private static final long serialVersionUID = 1L; - - @Override - public boolean end(MavenBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { - assertNotNull(build.getWorkspace()); - return true; - } - } -} \ No newline at end of file diff --git a/test/src/test/java/hudson/maven/MavenJellyTest.java b/test/src/test/java/hudson/maven/MavenJellyTest.java deleted file mode 100644 index d501362862..0000000000 --- a/test/src/test/java/hudson/maven/MavenJellyTest.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2004-2009, Sun Microsystems, 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.maven; - -import hudson.remoting.Which; -import junit.framework.Test; -import junit.framework.TestCase; -import org.jvnet.hudson.test.JellyTestSuiteBuilder; - -/** - * Runs Jelly checks on maven-plugin. - * - * @author Andrew Bayer - */ -public class MavenJellyTest extends TestCase { - public static Test suite() throws Exception { - return JellyTestSuiteBuilder.build(Which.jarFile(MavenModuleSet.class),true); - } -} diff --git a/test/src/test/java/hudson/maven/MavenModuleSetTest.java b/test/src/test/java/hudson/maven/MavenModuleSetTest.java deleted file mode 100644 index 2b379d7c01..0000000000 --- a/test/src/test/java/hudson/maven/MavenModuleSetTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package hudson.maven; - -import hudson.maven.local_repo.PerJobLocalRepositoryLocator; -import hudson.model.Item; -import org.jvnet.hudson.test.Bug; - -import org.jvnet.hudson.test.HudsonTestCase; - -/** - * @author Kohsuke Kawaguchi - */ -public class MavenModuleSetTest extends HudsonTestCase { - public void testConfigRoundtripLocalRepository() throws Exception { - MavenModuleSet p = createMavenProject(); - configRoundtrip((Item) p); - - assertNull(p.getExplicitLocalRepository()); - - // make sure it roundtrips - PerJobLocalRepositoryLocator before = new PerJobLocalRepositoryLocator(); - p.setLocalRepository(before); - configRoundtrip((Item)p); - assertEqualDataBoundBeans(p.getLocalRepository(),before); - assertTrue(before!=p.getLocalRepository()); - } - - @Bug(17402) - public void testGetItem() throws Exception { - assertNull(createMavenProject().getItem("invalid")); - } - -} diff --git a/test/src/test/java/hudson/maven/MavenMultiModuleLogRotatorCleanArtifactsTest.java b/test/src/test/java/hudson/maven/MavenMultiModuleLogRotatorCleanArtifactsTest.java deleted file mode 100644 index 114c783552..0000000000 --- a/test/src/test/java/hudson/maven/MavenMultiModuleLogRotatorCleanArtifactsTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/** - * - */ -package hudson.maven; - -import hudson.FilePath; -import hudson.Launcher; -import hudson.maven.reporters.MavenFingerprinter; -import hudson.model.BuildListener; -import hudson.tasks.LogRotator; -import hudson.tasks.Maven.MavenInstallation; - -import java.io.File; -import java.io.IOException; -import java.util.Collection; - -import org.apache.commons.io.FileUtils; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.jvnet.hudson.test.Bug; -import org.jvnet.hudson.test.ExtractResourceWithChangesSCM; -import org.jvnet.hudson.test.For; -import org.jvnet.hudson.test.JenkinsRule; - -/** - * - * Test that looks in jobs archive with 2 builds. When LogRotator set as build - * discarder with settings to keep only 1 build with artifacts, test searches - * for jars in archive for build one and build two, expecting no jars in build 1 - * and expecting jars in build 2. - * - * - */ -public class MavenMultiModuleLogRotatorCleanArtifactsTest { - - - @Rule - public JenkinsRule j = new JenkinsRule(); - private MavenModuleSet m; - private FilePath jobs; - - private static class TestReporter extends MavenReporter { - /** - * - */ - private static final long serialVersionUID = 1L; - - @Override - public boolean end(MavenBuild build, Launcher launcher, - BuildListener listener) throws InterruptedException, - IOException { - Assert.assertNotNull(build.getProject().getSomeWorkspace()); - Assert.assertNotNull(build.getWorkspace()); - return true; - } - } - - @Before - public void setUp() throws Exception { - j.configureDefaultMaven("apache-maven-2.2.1", MavenInstallation.MAVEN_21); - m = j.createMavenProject(); - m.setBuildDiscarder(new LogRotator("-1", "2", "-1", "1")); - m.getReporters().add(new TestReporter()); - m.getReporters().add(new MavenFingerprinter()); - m.setScm(new ExtractResourceWithChangesSCM(getClass().getResource( - "maven-multimod.zip"), getClass().getResource( - "maven-multimod-changes.zip"))); - j.buildAndAssertSuccess(m); - // Now run a second build with the changes. - m.setIncrementalBuild(false); - j.buildAndAssertSuccess(m); - FilePath workspace = m.getSomeWorkspace(); - FilePath parent = workspace.getParent().getParent(); - jobs = new FilePath(parent, "jobs"); - } - - @Test - @Bug(17508) - @For({MavenModuleSetBuild.class, LogRotator.class}) - @SuppressWarnings("unchecked") - public void testArtifactsAreDeletedInBuildOneWhenBuildDiscarderRun() - throws Exception { - File directory = new File(new FilePath(jobs, "test0/builds/1").getRemote()); - Collection files = FileUtils.listFiles(directory, - new String[] { "jar" }, true); - Assert.assertTrue( - "Found jars in previous build, that should not happen", - files.isEmpty()); - Collection files2 = FileUtils.listFiles(new File(new FilePath( - jobs, "test0/builds/2").getRemote()), new String[] { "jar" }, true); - Assert.assertFalse("No jars in last build ALERT!", files2.isEmpty()); - } - - /** - * Performs a third build and expecting build one to be deleted - * @throws Exception - */ - @For({MavenModuleSetBuild.class, LogRotator.class}) - @Test - public void testArtifactsOldBuildsDeletedWhenBuildDiscarderRun() - throws Exception { - j.buildAndAssertSuccess(m); - File directory = new File(new FilePath(jobs, "test0/builds/1").getRemote()); - Assert.assertFalse("oops the build should have been deleted", directory.exists()); - } - -} diff --git a/test/src/test/java/hudson/maven/MavenMultiModuleTest.java b/test/src/test/java/hudson/maven/MavenMultiModuleTest.java deleted file mode 100644 index 91137ba101..0000000000 --- a/test/src/test/java/hudson/maven/MavenMultiModuleTest.java +++ /dev/null @@ -1,622 +0,0 @@ -package hudson.maven; - -import com.gargoylesoftware.htmlunit.html.HtmlPage; -import hudson.FilePath; -import hudson.Functions; -import org.junit.Assert; -import org.jvnet.hudson.test.Bug; -import org.jvnet.hudson.test.ExtractResourceSCM; -import org.jvnet.hudson.test.ExtractResourceWithChangesSCM; -import org.jvnet.hudson.test.ExtractChangeLogSet; - -import hudson.Launcher; -import hudson.Util; -import hudson.maven.reporters.MavenArtifactRecord; -import hudson.maven.reporters.MavenFingerprinter; -import hudson.model.AbstractBuild; -import hudson.model.BuildListener; -import hudson.model.Job; -import hudson.model.PermalinkProjectAction; -import hudson.model.Result; -import hudson.model.Run; -import hudson.tasks.Fingerprinter.FingerprintAction; -import hudson.tasks.Maven.MavenInstallation; -import java.io.File; - -import java.io.IOException; -import java.util.Collections; -import java.util.Map; -import java.util.TreeMap; -import jenkins.model.ArtifactManager; -import java.util.Set; -import java.util.TreeSet; -import jenkins.model.ArtifactManagerConfiguration; -import jenkins.model.ArtifactManagerFactory; -import jenkins.util.VirtualFile; -import static org.junit.Assert.*; -import org.junit.Assume; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.jvnet.hudson.test.JenkinsRule; - -/** - * @author Andrew Bayer - */ -public class MavenMultiModuleTest { - - @Rule public JenkinsRule j = new JenkinsRule(); - - /** - * NPE in {@code build.getProject().getWorkspace()} for {@link MavenBuild}. - */ - @Bug(4192) - @Test public void multiModMavenWsExists() throws Exception { - j.configureDefaultMaven("apache-maven-2.2.1", MavenInstallation.MAVEN_21); - MavenModuleSet m = j.createMavenProject(); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-multimod.zip"))); - assertFalse("MavenModuleSet.isNonRecursive() should be false", m.isNonRecursive()); - j.buildAndAssertSuccess(m); - } - - @Bug(18846) - @Test public void symlinksUpdated() throws Exception { - Assume.assumeFalse(Functions.isWindows()); - j.configureDefaultMaven(); - MavenModuleSet mms = j.createMavenProject(); - mms.setScm(new ExtractResourceSCM(MavenMultiModuleTest.class.getResource("maven-multimod.zip"))); - j.buildAndAssertSuccess(mms); - MavenModule mm = mms.getModule("org.jvnet.hudson.main.test.multimod:moduleA"); - j.buildAndAssertSuccess(mms); - assertEquals(2, mms.getLastStableBuild().number); - assertEquals(mms.getLastBuild().getId(), Util.resolveSymlink(new File(mms.getRootDir(), "builds/2"))); - assertEquals("2", Util.resolveSymlink(new File(mms.getRootDir(), "builds/lastStableBuild"))); - assertEquals("builds/lastStableBuild", Util.resolveSymlink(new File(mms.getRootDir(), "lastStable"))); - assertEquals("[lastBuild, lastStableBuild, lastSuccessfulBuild]", permalinks(mms).toString()); - assertEquals(2, mm.getLastStableBuild().number); - assertEquals(mm.getLastBuild().getId(), Util.resolveSymlink(new File(mm.getRootDir(), "builds/2"))); - assertEquals("2", Util.resolveSymlink(new File(mm.getRootDir(), "builds/lastStableBuild"))); - assertEquals("builds/lastStableBuild", Util.resolveSymlink(new File(mm.getRootDir(), "lastStable"))); - assertEquals("[lastBuild, lastStableBuild, lastSuccessfulBuild]", permalinks(mm).toString()); - } - private static Set permalinks(Job j) { - Set r = new TreeSet(); - for (PermalinkProjectAction.Permalink l : j.getPermalinks()) { - if (l.resolve(j) != null) { - r.add(l.getId()); - } - } - return r; - } - - @Test public void incrementalMultiModMaven() throws Exception { - j.configureDefaultMaven("apache-maven-2.2.1", MavenInstallation.MAVEN_21); - MavenModuleSet m = j.createMavenProject(); - m.getReporters().add(new TestReporter()); - m.getReporters().add(new MavenFingerprinter()); - m.setScm(new ExtractResourceWithChangesSCM(getClass().getResource("maven-multimod.zip"), - getClass().getResource("maven-multimod-changes.zip"))); - - j.buildAndAssertSuccess(m); - - // Now run a second build with the changes. - m.setIncrementalBuild(true); - j.buildAndAssertSuccess(m); - - MavenModuleSetBuild pBuild = m.getLastBuild(); - ExtractChangeLogSet changeSet = (ExtractChangeLogSet) pBuild.getChangeSet(); - - assertFalse("ExtractChangeLogSet should not be empty.", changeSet.isEmptySet()); - - for (MavenBuild modBuild : pBuild.getModuleLastBuilds().values()) { - String parentModuleName = modBuild.getParent().getModuleName().toString(); - if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod:moduleA")) { - assertEquals("moduleA should have Result.NOT_BUILT", Result.NOT_BUILT, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod:moduleB")) { - assertEquals("moduleB should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod:moduleC")) { - assertEquals("moduleC should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - } - - long summedModuleDuration = 0; - for (MavenBuild modBuild : pBuild.getModuleLastBuilds().values()) { - summedModuleDuration += modBuild.getDuration(); - } - assertTrue("duration of moduleset build should be greater-equal than sum of the module builds", - pBuild.getDuration() >= summedModuleDuration); - - assertFingerprintWereRecorded(pBuild); - } - - private void assertFingerprintWereRecorded(MavenModuleSetBuild modulesetBuild) { - boolean mustHaveFingerprints = false; - for (MavenBuild moduleBuild : modulesetBuild.getModuleLastBuilds().values()) { - if (moduleBuild.getResult() != Result.NOT_BUILT && moduleBuild.getResult() != Result.ABORTED) { - assertFingerprintWereRecorded(moduleBuild); - mustHaveFingerprints = true; - } - } - - if (mustHaveFingerprints) { - FingerprintAction action = modulesetBuild.getAction(FingerprintAction.class); - Assert.assertNotNull(action); - Assert.assertFalse(action.getFingerprints().isEmpty()); - } - } - - private void assertFingerprintWereRecorded(MavenBuild moduleBuild) { - FingerprintAction action = moduleBuild.getAction(FingerprintAction.class); - Assert.assertNotNull(action); - Assert.assertFalse(action.getFingerprints().isEmpty()); - - MavenArtifactRecord artifactRecord = moduleBuild.getAction(MavenArtifactRecord.class); - Assert.assertNotNull(artifactRecord); - String fingerprintName = artifactRecord.mainArtifact.groupId + ":" + artifactRecord.mainArtifact.fileName; - - Assert.assertTrue("Expected fingerprint " + fingerprintName + " in module build " + moduleBuild, - action.getFingerprints().containsKey(fingerprintName)); - - // we should assert more - i.e. that all dependencies are fingerprinted, too, - // but it's complicated to find out the dependencies of the build - } - - @Bug(5357) - @Test public void incrRelMultiModMaven() throws Exception { - j.configureDefaultMaven("apache-maven-2.2.1", MavenInstallation.MAVEN_21); - MavenModuleSet m = j.createMavenProject(); - m.setRootPOM("parent/pom.xml"); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceWithChangesSCM(getClass().getResource("maven-multimod-rel-base.zip"), - getClass().getResource("maven-multimod-changes.zip"))); - - j.buildAndAssertSuccess(m); - - // Now run a second build with the changes. - m.setIncrementalBuild(true); - j.buildAndAssertSuccess(m); - - MavenModuleSetBuild pBuild = m.getLastBuild(); - ExtractChangeLogSet changeSet = (ExtractChangeLogSet) pBuild.getChangeSet(); - - assertFalse("ExtractChangeLogSet should not be empty.", changeSet.isEmptySet()); - - for (MavenBuild modBuild : pBuild.getModuleLastBuilds().values()) { - String parentModuleName = modBuild.getParent().getModuleName().toString(); - if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod:moduleA")) { - assertEquals("moduleA should have Result.NOT_BUILT", Result.NOT_BUILT, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod:moduleB")) { - assertEquals("moduleB should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod:moduleC")) { - assertEquals("moduleC should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - } - - long summedModuleDuration = 0; - for (MavenBuild modBuild : pBuild.getModuleLastBuilds().values()) { - summedModuleDuration += modBuild.getDuration(); - } - assertTrue("duration of moduleset build should be greater-equal than sum of the module builds", - pBuild.getDuration() >= summedModuleDuration); - } - - - @Bug(6544) - @Ignore("kutzi 10/10/11 ignore test until I can figure out why it fails sometimes") - @Test public void estimatedDurationForIncrementalMultiModMaven() - throws Exception { - j.configureDefaultMaven("apache-maven-2.2.1", MavenInstallation.MAVEN_21); - MavenModuleSet m = j.createMavenProject(); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceWithChangesSCM(getClass().getResource( - "maven-multimod.zip"), getClass().getResource( - "maven-multimod-changes.zip"))); - - j.buildAndAssertSuccess(m); - - // Now run a second, incremental build with the changes. - m.setIncrementalBuild(true); - j.buildAndAssertSuccess(m); - - MavenModuleSetBuild lastBuild = m.getLastBuild(); - MavenModuleSetBuild previousBuild = lastBuild.getPreviousBuild(); - assertNull("There should be only one previous build", previousBuild.getPreviousBuild()); - - // Since the estimated duration is calculated based on the previous builds - // and there was only one previous build (which built all modules) and this build - // did only build one module, the estimated duration of this build must be - // smaller than the duration of the previous build. - // (It's highly unlikely that the durations are equal, but I've already seen it fail. - // Therefore <= instead of <) - assertTrue("Estimated duration should be <= " + previousBuild.getDuration() - + ", but is " + lastBuild.getEstimatedDuration(), - lastBuild.getEstimatedDuration() <= previousBuild.getDuration()); - } - - /** - * NPE in {@code getChangeSetFor(m)} in {@link MavenModuleSetBuild} when incremental build is - * enabled and a new module is added. - */ - @Test public void newModMultiModMaven() throws Exception { - j.configureDefaultMaven("apache-maven-2.2.1", MavenInstallation.MAVEN_21); - MavenModuleSet m = j.createMavenProject(); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceWithChangesSCM(getClass().getResource("maven-multimod.zip"), - getClass().getResource("maven-multimod-changes.zip"))); - - m.setIncrementalBuild(true); - j.buildAndAssertSuccess(m); - } - - /** - * When "-N' or "--non-recursive" show up in the goals, any child modules should be ignored. - */ - @Bug(4491) - @Test public void multiModMavenNonRecursiveParsing() throws Exception { - j.configureDefaultMaven("apache-maven-2.2.1", MavenInstallation.MAVEN_21); - MavenModuleSet m = j.createMavenProject(); - m.setGoals("clean install -N"); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-multimod.zip"))); - - j.buildAndAssertSuccess(m); - - MavenModuleSetBuild pBuild = m.getLastBuild(); - - for (MavenBuild modBuild : pBuild.getModuleLastBuilds().values()) { - String parentModuleName = modBuild.getParent().getModuleName().toString(); - if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod:multimod-top")) { - assertEquals("moduleA should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod:moduleA")) { - assertEquals("moduleA should have Result.NOT_BUILT", Result.NOT_BUILT, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod:moduleB")) { - assertEquals("moduleB should have Result.NOT_BUILT", Result.NOT_BUILT, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod:moduleC")) { - assertEquals("moduleC should have Result.NOT_BUILT", Result.NOT_BUILT, modBuild.getResult()); - } - - } - - } - - /** - * Module failures in build X should lead to those modules being re-run in build X+1, even if - * incremental build is enabled and nothing changed in those modules. - */ - @Bug(4152) - @Test public void incrementalMultiModWithErrorsMaven() throws Exception { - j.configureDefaultMaven("apache-maven-2.2.1", MavenInstallation.MAVEN_21); - MavenModuleSet m = j.createMavenProject(); - m.setIncrementalBuild(true); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceWithChangesSCM(getClass().getResource("maven-multimod-incr.zip"), - getClass().getResource("maven-multimod-changes.zip"))); - - j.assertBuildStatus(Result.UNSTABLE, m.scheduleBuild2(0).get()); - MavenModuleSetBuild pBuild = m.getLastBuild(); - - for (MavenBuild modBuild : pBuild.getModuleLastBuilds().values()) { - String parentModuleName = modBuild.getParent().getModuleName().toString(); - if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleA")) { - assertEquals("moduleA should have Result.UNSTABLE", Result.UNSTABLE, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleB")) { - assertEquals("moduleB should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleC")) { - assertEquals("moduleC should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleD")) { - assertEquals("moduleD should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - } - - // Now run a second build with the changes. - j.assertBuildStatus(Result.UNSTABLE, m.scheduleBuild2(0).get()); - - pBuild = m.getLastBuild(); - ExtractChangeLogSet changeSet = (ExtractChangeLogSet) pBuild.getChangeSet(); - - assertFalse("ExtractChangeLogSet should not be empty.", changeSet.isEmptySet()); - // changelog contains a change for module B - assertEquals("Parent build should have Result.UNSTABLE", Result.UNSTABLE, pBuild.getResult()); - - for (MavenBuild modBuild : pBuild.getModuleLastBuilds().values()) { - String parentModuleName = modBuild.getParent().getModuleName().toString(); - // A must be build again, because it was UNSTABLE before - if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleA")) { - assertEquals("moduleA should have Result.UNSTABLE", Result.UNSTABLE, modBuild.getResult()); - } - // B must be build, because it has changes - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleB")) { - assertEquals("moduleB should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - // C must be build, because it depends on B - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleC")) { - assertEquals("moduleC should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - // D must not be build - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleD")) { - assertEquals("moduleD should have Result.NOT_BUILT", Result.NOT_BUILT, modBuild.getResult()); - } - } - } - - /** - * If "deploy modules" is checked and aggregator build failed - * then all modules build this time, have to be build next time, again. - */ - @Bug(5121) - @Test public void incrementalRedeployAfterAggregatorError() throws Exception { - j.configureDefaultMaven("apache-maven-2.2.1", MavenInstallation.MAVEN_21); - MavenModuleSet m = j.createMavenProject(); - m.setIncrementalBuild(true); - m.getReporters().add(new TestReporter()); - m.getPublishers().add(new DummyRedeployPublisher()); - m.setScm(new ExtractResourceWithChangesSCM(getClass().getResource("maven-multimod-incr.zip"), - getClass().getResource("maven-multimod-changes.zip"))); - - j.assertBuildStatus(Result.UNSTABLE, m.scheduleBuild2(0).get()); - MavenModuleSetBuild pBuild = m.getLastBuild(); - - for (MavenBuild modBuild : pBuild.getModuleLastBuilds().values()) { - String parentModuleName = modBuild.getParent().getModuleName().toString(); - if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleA")) { - assertEquals("moduleA should have Result.UNSTABLE", Result.UNSTABLE, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleB")) { - assertEquals("moduleB should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleC")) { - assertEquals("moduleC should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleD")) { - assertEquals("moduleD should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - } - - // Now run a second build. - j.assertBuildStatus(Result.UNSTABLE, m.scheduleBuild2(0).get()); - - pBuild = m.getLastBuild(); - ExtractChangeLogSet changeSet = (ExtractChangeLogSet) pBuild.getChangeSet(); - - assertFalse("ExtractChangeLogSet should not be empty.", changeSet.isEmptySet()); - // changelog contains a change for module B - assertEquals("Parent build should have Result.UNSTABLE", Result.UNSTABLE, pBuild.getResult()); - - for (MavenBuild modBuild : pBuild.getModuleLastBuilds().values()) { - String parentModuleName = modBuild.getParent().getModuleName().toString(); - // A must be build again, because it was UNSTABLE before - if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleA")) { - assertEquals("moduleA should have Result.UNSTABLE", Result.UNSTABLE, modBuild.getResult()); - } - // B must be build, because it has changes - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleB")) { - assertEquals("moduleB should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - // C must be build, because it depends on B - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleC")) { - assertEquals("moduleC should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - // D must be build again, because it needs to be deployed now - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleD")) { - assertEquals("moduleD should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - } - } - - /** - * Test failures in a child module should lead to the parent being marked as unstable. - */ - @Bug(4378) - @Test public void multiModWithTestFailuresMaven() throws Exception { - j.configureDefaultMaven("apache-maven-2.2.1", MavenInstallation.MAVEN_21); - MavenModuleSet m = j.createMavenProject(); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-multimod-incr.zip"))); - - j.assertBuildStatus(Result.UNSTABLE, m.scheduleBuild2(0).get()); - - MavenModuleSetBuild pBuild = m.getLastBuild(); - - assertEquals("Parent build should have Result.UNSTABLE", Result.UNSTABLE, pBuild.getResult()); - - for (MavenBuild modBuild : pBuild.getModuleLastBuilds().values()) { - String parentModuleName = modBuild.getParent().getModuleName().toString(); - if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleA")) { - assertEquals("moduleA should have Result.UNSTABLE", Result.UNSTABLE, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleB")) { - assertEquals("moduleB should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleC")) { - assertEquals("moduleC should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod.incr:moduleD")) { - assertEquals("moduleD should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - } - } - - @Bug(8484) - @Test public void multiModMavenNonRecursive() throws Exception { - j.configureDefaultMaven("apache-maven-2.2.1", MavenInstallation.MAVEN_21); - MavenModuleSet m = j.createMavenProject(); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-multimod.zip"))); - m.setGoals( "-N validate" ); - assertTrue("MavenModuleSet.isNonRecursive() should be true", m.isNonRecursive()); - j.buildAndAssertSuccess(m); - assertEquals("not only one module", 1, m.getModules().size()); - } - - @Bug(17713) - @Test public void modulesPageLinks() throws Exception { - j.configureMaven3(); - MavenModuleSet ms = j.createMavenProject(); - ms.setScm(new ExtractResourceSCM(getClass().getResource("maven-multimod.zip"))); - j.buildAndAssertSuccess(ms); - MavenModule m = ms.getModule("org.jvnet.hudson.main.test.multimod:moduleA"); - assertNotNull(m); - assertEquals(1, m.getLastBuild().getNumber()); - JenkinsRule.WebClient wc = j.createWebClient(); - HtmlPage modulesPage = wc.getPage(ms, "modules"); - modulesPage.getAnchorByText(m.getDisplayName()).openLinkInNewWindow(); - } - - @Bug(17236) - @Test public void artifactArchiving() throws Exception { - ArtifactManagerConfiguration.get().getArtifactManagerFactories().add(new TestAMF()); - j.configureDefaultMaven(); // using Maven 2 so we can test single-module builds - MavenModuleSet mms = j.createMavenProject(); - mms.setScm(new ExtractResourceSCM(getClass().getResource("maven-multimod.zip"))); - mms.setAssignedNode(j.createOnlineSlave()); - j.buildAndAssertSuccess(mms); - // We want all the artifacts in a given module to be archived in one operation. But modules are archived separately. - Map> expected = new TreeMap>(); - FilePath ws = mms.getModule("org.jvnet.hudson.main.test.multimod$multimod-top").getBuildByNumber(1).getWorkspace(); - expected.put("org.jvnet.hudson.main.test.multimod:multimod-top", Collections.singletonMap("org.jvnet.hudson.main.test.multimod/multimod-top/1.0-SNAPSHOT/multimod-top-1.0-SNAPSHOT.pom", new FilePath(ws.getChannel(), "…/org/jvnet/hudson/main/test/multimod/multimod-top/1.0-SNAPSHOT/multimod-top-1.0-SNAPSHOT.pom"))); - for (String module : new String[] {"moduleA", "moduleB", "moduleC"}) { - Map m = new TreeMap(); - ws = mms.getModule("org.jvnet.hudson.main.test.multimod$" + module).getBuildByNumber(1).getWorkspace(); - m.put("org.jvnet.hudson.main.test.multimod/" + module + "/1.0-SNAPSHOT/" + module + "-1.0-SNAPSHOT.pom", ws.child("pom.xml")); - m.put("org.jvnet.hudson.main.test.multimod/" + module + "/1.0-SNAPSHOT/" + module + "-1.0-SNAPSHOT.jar", ws.child("target/" + module + "-1.0-SNAPSHOT.jar")); - expected.put("org.jvnet.hudson.main.test.multimod:" + module, m); - } - assertEquals(expected.toString(), TestAM.archivings.toString()); // easy to read - assertEquals(expected, TestAM.archivings); // compares also FileChannel - // Also check single-module build. - expected.clear(); - TestAM.archivings.clear(); - MavenBuild isolated = j.buildAndAssertSuccess(mms.getModule("org.jvnet.hudson.main.test.multimod$moduleA")); - assertEquals(2, isolated.number); - Map m = new TreeMap(); - ws = isolated.getWorkspace(); - m.put("org.jvnet.hudson.main.test.multimod/moduleA/1.0-SNAPSHOT/moduleA-1.0-SNAPSHOT.pom", ws.child("pom.xml")); - m.put("org.jvnet.hudson.main.test.multimod/moduleA/1.0-SNAPSHOT/moduleA-1.0-SNAPSHOT.jar", ws.child("target/moduleA-1.0-SNAPSHOT.jar")); - expected.put("org.jvnet.hudson.main.test.multimod:moduleA", m); - assertEquals(expected, TestAM.archivings); - } - - public static final class TestAMF extends ArtifactManagerFactory { - @Override public ArtifactManager managerFor(Run build) { - return new TestAM(build); - } - } - public static final class TestAM extends ArtifactManager { - static final Map> archivings = new TreeMap>(); - transient Run build; - TestAM(Run build) { - onLoad(build); - } - @Override public void onLoad(Run build) { - this.build = build; - } - @Override public void archive(FilePath workspace, Launcher launcher, BuildListener listener, Map artifacts) throws IOException, InterruptedException { - String name = build.getParent().getName(); - if (archivings.containsKey(name)) { - // Would be legitimate only if some archived files for a given module were outside workspace, such as repository parent POM, *and* others were inside, which is not the case in this test. - throw new IOException("repeated archiving to " + name); - } - Map m = new TreeMap(); - for (Map.Entry e : artifacts.entrySet()) { - FilePath f = workspace.child(e.getValue()); - if (f.exists()) { - if (f.getRemote().replace('\\', '/').contains("/org/jvnet/hudson/main/test/")) { - // Inside the local repository. Hard to know exactly what that path might be, so just mask it out. - f = new FilePath(f.getChannel(), f.getRemote().replaceFirst("^.+(?=[/\\\\]org[/\\\\]jvnet[/\\\\]hudson[/\\\\]main[/\\\\]test[/\\\\])", "…")); - } - m.put(e.getKey(), f); - } else { - throw new IOException("no such file " + f); - } - } - archivings.put(name, m); - } - @Override public boolean delete() throws IOException, InterruptedException { - throw new IOException(); - } - @Override public VirtualFile root() { - throw new UnsupportedOperationException(); - } - } - - /* - @Test public void parallelMultiModMavenWsExists() throws Exception { - configureDefaultMaven(); - MavenModuleSet m = createMavenProject(); - m.setAggregatorStyleBuild(false); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-multimod.zip"))); - assertBuildStatusSuccess(m.scheduleBuild2(0).get()); - - for (MavenModule mod : m.sortedActiveModules) { - while (mod.getLastBuild() == null) { - Thread.sleep(500); - } - - while (mod.getLastBuild().isBuilding()) { - Thread.sleep(500); - } - - assertBuildStatusSuccess(mod.getLastBuild()); - } - - - - } - - @Test public void privateRepoParallelMultiModMavenWsExists() throws Exception { - configureDefaultMaven(); - MavenModuleSet m = createMavenProject(); - m.setAggregatorStyleBuild(false); - m.setUsePrivateRepository(true); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-multimod.zip"))); - assertBuildStatusSuccess(m.scheduleBuild2(0).get()); - - for (MavenModule mod : m.sortedActiveModules) { - while (mod.getLastBuild() == null) { - Thread.sleep(500); - } - - while (mod.getLastBuild().isBuilding()) { - Thread.sleep(500); - } - - assertBuildStatusSuccess(mod.getLastBuild()); - } - - } - */ - private static class TestReporter extends MavenReporter { - @Override - public boolean end(MavenBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { - assertNotNull(build.getProject().getWorkspace()); - assertNotNull(build.getWorkspace()); - return true; - } - } - - private static class DummyRedeployPublisher extends RedeployPublisher { - public DummyRedeployPublisher() { - super("", "", false, false); - } - - @Override - public boolean perform(AbstractBuild build, Launcher launcher, - BuildListener listener) throws InterruptedException, - IOException { - return true; - } - } -} diff --git a/test/src/test/java/hudson/maven/MavenMultiModuleTestIncremental.java b/test/src/test/java/hudson/maven/MavenMultiModuleTestIncremental.java deleted file mode 100755 index 2ebaee49c2..0000000000 --- a/test/src/test/java/hudson/maven/MavenMultiModuleTestIncremental.java +++ /dev/null @@ -1,70 +0,0 @@ -package hudson.maven; - -import hudson.Launcher; -import hudson.model.BuildListener; -import hudson.model.Result; -import hudson.tasks.Maven.MavenInstallation; - -import java.io.IOException; - -import org.jvnet.hudson.test.Bug; -import org.jvnet.hudson.test.ExtractChangeLogSet; -import org.jvnet.hudson.test.ExtractResourceWithChangesSCM; -import org.jvnet.hudson.test.HudsonTestCase; - -/** - * @author Andrew Bayer - */ -public class MavenMultiModuleTestIncremental extends HudsonTestCase { - - @Bug(7684) - public void testRelRootPom() throws Exception { - configureDefaultMaven("apache-maven-2.2.1", MavenInstallation.MAVEN_21); - MavenModuleSet m = createMavenProject(); - m.setRootPOM("parent/pom.xml"); - m.getReporters().add(new TestReporter()); - m.setScm(new ExtractResourceWithChangesSCM(getClass().getResource("maven-multimod-rel-base.zip"), - getClass().getResource("maven-multimod-changes.zip"))); - - buildAndAssertSuccess(m); - - // Now run a second build with the changes. - m.setIncrementalBuild(true); - buildAndAssertSuccess(m); - - MavenModuleSetBuild pBuild = m.getLastBuild(); - ExtractChangeLogSet changeSet = (ExtractChangeLogSet) pBuild.getChangeSet(); - - assertFalse("ExtractChangeLogSet should not be empty.", changeSet.isEmptySet()); - - for (MavenBuild modBuild : pBuild.getModuleLastBuilds().values()) { - String parentModuleName = modBuild.getParent().getModuleName().toString(); - if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod:moduleA")) { - assertEquals("moduleA should have Result.NOT_BUILT", Result.NOT_BUILT, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod:moduleB")) { - assertEquals("moduleB should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - else if (parentModuleName.equals("org.jvnet.hudson.main.test.multimod:moduleC")) { - assertEquals("moduleC should have Result.SUCCESS", Result.SUCCESS, modBuild.getResult()); - } - } - - long summedModuleDuration = 0; - for (MavenBuild modBuild : pBuild.getModuleLastBuilds().values()) { - summedModuleDuration += modBuild.getDuration(); - } - assertTrue("duration of moduleset build should be greater-equal than sum of the module builds", - pBuild.getDuration() >= summedModuleDuration); - } - - private static class TestReporter extends MavenReporter { - private static final long serialVersionUID = 1L; - - @Override - public boolean end(MavenBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { - assertNotNull(build.getWorkspace()); - return true; - } - } -} diff --git a/test/src/test/java/hudson/maven/MavenOptsTest.java b/test/src/test/java/hudson/maven/MavenOptsTest.java deleted file mode 100644 index a86bd1969a..0000000000 --- a/test/src/test/java/hudson/maven/MavenOptsTest.java +++ /dev/null @@ -1,127 +0,0 @@ -package hudson.maven; - -import hudson.maven.MavenModuleSet.DescriptorImpl; -import org.jvnet.hudson.test.HudsonTestCase; -import org.jvnet.hudson.test.Bug; -import org.jvnet.hudson.test.ExtractResourceSCM; -import hudson.EnvVars; -import hudson.model.Result; -import hudson.tasks.Maven.MavenInstallation; - -/** - * @author Andrew Bayer - */ -public class MavenOptsTest extends HudsonTestCase { - DescriptorImpl d; - - @Override - protected void setUp() throws Exception { - super.setUp(); - d = jenkins.getDescriptorByType(DescriptorImpl.class); - } - - @Override - protected void tearDown() throws Exception { - d.setGlobalMavenOpts(null); - super.tearDown(); - } - - public void testEnvMavenOptsNoneInProject() throws Exception { - configureDefaultMaven(); - MavenModuleSet m = createMavenProject(); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-opts-echo.zip"))); - m.setGoals("validate"); - m.setAssignedLabel(createSlave(new EnvVars("MAVEN_OPTS", "-Dhudson.mavenOpt.test=foo")).getSelfLabel()); - - buildAndAssertSuccess(m); - - assertLogContains("[hudson.mavenOpt.test=foo]", m.getLastBuild()); - } - - - public void testEnvMavenOptsOverriddenByProject() throws Exception { - configureDefaultMaven(); - MavenModuleSet m = createMavenProject(); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-opts-echo.zip"))); - m.setGoals("validate"); - m.setMavenOpts("-Dhudson.mavenOpt.test=bar"); - m.setAssignedLabel(createSlave(new EnvVars("MAVEN_OPTS", "-Dhudson.mavenOpt.test=foo")).getSelfLabel()); - - buildAndAssertSuccess(m); - - assertLogContains("[hudson.mavenOpt.test=bar]", m.getLastBuild()); - } - - public void testEnvAndGlobalMavenOptsOverriddenByProject() throws Exception { - configureDefaultMaven(); - MavenModuleSet m = createMavenProject(); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-opts-echo.zip"))); - m.setGoals("validate"); - d.setGlobalMavenOpts("-Dhudson.mavenOpt.test=bar"); - m.setAssignedLabel(createSlave(new EnvVars("MAVEN_OPTS", "-Dhudson.mavenOpt.test=foo")).getSelfLabel()); - m.setMavenOpts("-Dhudson.mavenOpt.test=baz"); - - buildAndAssertSuccess(m); - - assertLogContains("[hudson.mavenOpt.test=baz]", m.getLastBuild()); - } - - - public void testGlobalMavenOpts() throws Exception { - configureDefaultMaven(); - MavenModuleSet m = createMavenProject(); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-opts-echo.zip"))); - m.setGoals("validate"); - d.setGlobalMavenOpts("-Dhudson.mavenOpt.test=bar"); - - buildAndAssertSuccess(m); - - assertLogContains("[hudson.mavenOpt.test=bar]", m.getLastBuild()); - } - - public void testGlobalMavenOptsOverridenByProject() throws Exception { - configureDefaultMaven(); - MavenModuleSet m = createMavenProject(); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-opts-echo.zip"))); - m.setGoals("validate"); - d.setGlobalMavenOpts("-Dhudson.mavenOpt.test=bar"); - m.setMavenOpts("-Dhudson.mavenOpt.test=foo"); - - buildAndAssertSuccess(m); - - assertLogContains("[hudson.mavenOpt.test=foo]", m.getLastBuild()); - } - - @Bug(5651) - public void testNewlinesInOptsRemoved() throws Exception { - configureDefaultMaven("apache-maven-2.2.1", MavenInstallation.MAVEN_21); - MavenModuleSet m = createMavenProject(); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-surefire-unstable.zip"))); - m.setMavenOpts("-XX:MaxPermSize=512m\r\n-Xms128m\r\n-Xmx512m"); - m.setGoals("install"); - - assertBuildStatus(Result.UNSTABLE, m.scheduleBuild2(0).get()); - MavenModuleSetBuild pBuild = m.getLastBuild(); - - assertEquals("Parent build should have Result.UNSTABLE", Result.UNSTABLE, pBuild.getResult()); - - } - - /** - * Makes sure that environment variables in MAVEN_OPTS are properly expanded. - */ - public void testEnvironmentVariableExpansion() throws Exception { - configureDefaultMaven(); - MavenModuleSet m = createMavenProject(); - m.setMavenOpts("$FOO"); - m.setScm(new ExtractResourceSCM(getClass().getResource("maven-opts-echo.zip"))); - m.setGoals("validate"); - m.setAssignedLabel(createSlave(new EnvVars("FOO", "-Dhudson.mavenOpt.test=foo -Dhudson.mavenOpt.test2=bar")).getSelfLabel()); - - buildAndAssertSuccess(m); - - assertLogContains("[hudson.mavenOpt.test=foo]", m.getLastBuild()); - } - -} - diff --git a/test/src/test/java/hudson/maven/MavenProjectTest.java b/test/src/test/java/hudson/maven/MavenProjectTest.java deleted file mode 100644 index 0fbfd3ead3..0000000000 --- a/test/src/test/java/hudson/maven/MavenProjectTest.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2004-2010, Sun Microsystems, 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.maven; - -import hudson.maven.local_repo.DefaultLocalRepositoryLocator; -import hudson.maven.local_repo.PerJobLocalRepositoryLocator; -import hudson.model.AbstractProject; -import hudson.model.Item; -import hudson.model.Result; -import hudson.tasks.Maven.MavenInstallation; -import hudson.tasks.Shell; - -import java.io.File; - -import jenkins.model.Jenkins; -import jenkins.mvn.DefaultGlobalSettingsProvider; -import jenkins.mvn.DefaultSettingsProvider; -import jenkins.mvn.FilePathGlobalSettingsProvider; -import jenkins.mvn.FilePathSettingsProvider; -import jenkins.mvn.GlobalMavenConfig; - -import org.junit.Assert; -import org.jvnet.hudson.test.Bug; -import org.jvnet.hudson.test.ExtractResourceSCM; -import org.jvnet.hudson.test.HudsonTestCase; - -import java.net.HttpURLConnection; - -/** - * @author huybrechts - */ -public class MavenProjectTest extends HudsonTestCase { - - - public void testOnMaster() throws Exception { - MavenModuleSet project = createSimpleProject(); - project.setGoals("validate"); - - buildAndAssertSuccess(project); - } - - @Bug(16499) - public void testCopyFromExistingMavenProject() throws Exception { - MavenModuleSet project = createSimpleProject(); - project.setGoals("abcdefg"); - project.save(); - - MavenModuleSet copy = (MavenModuleSet) Jenkins.getInstance().copy((AbstractProject)project, "copy" + System.currentTimeMillis()); - assertNotNull("Copied project must not be null", copy); - assertEquals(project.getGoals(), copy.getGoals()); - } - - private MavenModuleSet createSimpleProject() throws Exception { - return createProject("/simple-projects.zip"); - } - - private MavenModuleSet createProject(final String scmResource) throws Exception { - MavenModuleSet project = createMavenProject(); - MavenInstallation mi = configureDefaultMaven(); - project.setScm(new ExtractResourceSCM(getClass().getResource( - scmResource))); - project.setMaven(mi.getName()); - project.setLocalRepository(new PerJobLocalRepositoryLocator()); - return project; - } - - public void testOnSlave() throws Exception { - MavenModuleSet project = createSimpleProject(); - project.setGoals("validate"); - project.setAssignedLabel(createSlave().getSelfLabel()); - - buildAndAssertSuccess(project); - } - - /** - * Check if the generated site is linked correctly. - */ - @Bug(3497) - public void testSiteBuild() throws Exception { - MavenModuleSet project = createSimpleProject(); - project.setGoals("site"); - - buildAndAssertSuccess(project); - - // this should succeed - HudsonTestCase.WebClient wc = new WebClient(); - wc.getPage(project,"site"); - wc.assertFails(project.getUrl() + "site/no-such-file", HttpURLConnection.HTTP_NOT_FOUND); - } - - /** - * Check if the generated site is linked correctly for multi module projects. - */ - public void testMultiModuleSiteBuild() throws Exception { - MavenModuleSet project = createProject("maven-multimodule-site.zip"); - project.setGoals("site"); - - buildAndAssertSuccess(project); - - // this should succeed - HudsonTestCase.WebClient wc = new WebClient(); - wc.getPage(project, "site"); - wc.getPage(project, "site/core"); - wc.getPage(project, "site/client"); - - //@Bug(7577): check that site generation succeeds also if only a single module is build - MavenModule coreModule = project.getModule("mmtest:core"); - Assert.assertEquals("site", coreModule.getGoals()); - buildAndAssertSuccess(coreModule); - wc.getPage(project, "site/core"); - } - - /** - * Check if the the site goal will work when run from a slave. - */ - @Bug(5943) - public void testMultiModuleSiteBuildOnSlave() throws Exception { - MavenModuleSet project = createProject("maven-multimodule-site.zip"); - project.setGoals("site"); - project.setAssignedLabel(createSlave().getSelfLabel()); - - buildAndAssertSuccess(project); - - // this should succeed - HudsonTestCase.WebClient wc = new WebClient(); - wc.getPage(project, "site"); - wc.getPage(project, "site/core"); - wc.getPage(project, "site/client"); - } - - @Bug(6779) - public void testDeleteSetBuildDeletesModuleBuilds() throws Exception { - MavenModuleSet project = createProject("maven-multimod.zip"); - project.setLocalRepository(new DefaultLocalRepositoryLocator()); - project.setGoals("install"); - buildAndAssertSuccess(project); - buildAndAssertSuccess(project.getModule("org.jvnet.hudson.main.test.multimod:moduleB")); - buildAndAssertSuccess(project); - assertEquals(2, project.getBuilds().size()); // Module build does not add a ModuleSetBuild - project.getFirstBuild().delete(); - // A#1, B#1 and B#2 should all be deleted too - assertEquals(1, project.getModule("org.jvnet.hudson.main.test.multimod:moduleA").getBuilds().size()); - assertEquals(1, project.getModule("org.jvnet.hudson.main.test.multimod:moduleB").getBuilds().size()); - } - @Bug(7261) - public void testAbsolutePathPom() throws Exception { - File pom = new File(this.getClass().getResource("test-pom-7162.xml").toURI()); - MavenModuleSet project = createMavenProject(); - MavenInstallation mi = configureDefaultMaven(); - project.setMaven(mi.getName()); - project.setRootPOM(pom.getAbsolutePath()); - project.setGoals("install"); - buildAndAssertSuccess(project); - } - - @Bug(17177) - public void testCorrectResultInPostStepAfterFailedPreBuildStep() throws Exception { - MavenModuleSet p = createSimpleProject(); - MavenInstallation mi = configureDefaultMaven(); - p.setMaven(mi.getName()); - p.setGoals("initialize"); - - Shell pre = new Shell("exit 1"); // must fail to simulate scenario! - p.getPrebuilders().add(pre); - ResultExposingBuilder resultExposer = new ResultExposingBuilder(); - p.getPostbuilders().add(resultExposer); - - assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); - assertEquals("The result passed to the post build step was not the one from the pre build step", Result.FAILURE, resultExposer.getResult()); - } - - - /** - * Config roundtrip test around pre/post build step - */ - public void testConfigRoundtrip() throws Exception { - MavenModuleSet m = createMavenProject(); - Shell b1 = new Shell("1"); - Shell b2 = new Shell("2"); - m.getPrebuilders().add(b1); - m.getPostbuilders().add(b2); - configRoundtrip((Item)m); - - assertEquals(1, m.getPrebuilders().size()); - assertNotSame(b1,m.getPrebuilders().get(Shell.class)); - assertEquals("1",m.getPrebuilders().get(Shell.class).getCommand()); - - assertEquals(1, m.getPostbuilders().size()); - assertNotSame(b2,m.getPostbuilders().get(Shell.class)); - assertEquals("2",m.getPostbuilders().get(Shell.class).getCommand()); - - for (Result r : new Result[]{Result.SUCCESS, Result.UNSTABLE, Result.FAILURE}) { - m.setRunPostStepsIfResult(r); - configRoundtrip((Item)m); - assertEquals(r,m.getRunPostStepsIfResult()); - } - } - - - public void testDefaultSettingsProvider() throws Exception { - { - MavenModuleSet m = createMavenProject(); - - assertNotNull(m); - assertEquals(DefaultSettingsProvider.class, m.getSettings().getClass()); - assertEquals(DefaultGlobalSettingsProvider.class, m.getGlobalSettings().getClass()); - } - - { - GlobalMavenConfig globalMavenConfig = GlobalMavenConfig.get(); - assertNotNull("No global Maven Config available", globalMavenConfig); - globalMavenConfig.setSettingsProvider(new FilePathSettingsProvider("/tmp/settigns.xml")); - globalMavenConfig.setGlobalSettingsProvider(new FilePathGlobalSettingsProvider("/tmp/global-settigns.xml")); - - MavenModuleSet m = createMavenProject(); - assertEquals(FilePathSettingsProvider.class, m.getSettings().getClass()); - assertEquals("/tmp/settigns.xml", ((FilePathSettingsProvider)m.getSettings()).getPath()); - assertEquals("/tmp/global-settigns.xml", ((FilePathGlobalSettingsProvider)m.getGlobalSettings()).getPath()); - } - } -} diff --git a/test/src/test/java/hudson/maven/MavenSnapshotTriggerTest.java b/test/src/test/java/hudson/maven/MavenSnapshotTriggerTest.java deleted file mode 100644 index b898e29b18..0000000000 --- a/test/src/test/java/hudson/maven/MavenSnapshotTriggerTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package hudson.maven; - -import org.jvnet.hudson.test.ExtractResourceSCM; -import org.jvnet.hudson.test.HudsonTestCase; - -/** - * Tests that Maven jobs are triggered, when snapshot dependencies of them were build. - * - * @author Andrew Bayer - */ -public class MavenSnapshotTriggerTest extends HudsonTestCase { - /** - * Verifies dependency build ordering of SNAPSHOT dependency. - * Note - has to build the projects once each first in order to get dependency info. - */ - public void testSnapshotDependencyBuildTrigger() throws Exception { - - configureDefaultMaven(); - MavenModuleSet projA = createMavenProject("snap-dep-test-up"); - projA.setGoals("clean install"); - projA.setScm(new ExtractResourceSCM(getClass().getResource("maven-dep-test-A.zip"))); - MavenModuleSet projB = createMavenProject("snap-dep-test-down"); - projB.setGoals("clean install"); - projB.setIgnoreUpstremChanges(false); - projB.setQuietPeriod(0); - projB.setScm(new ExtractResourceSCM(getClass().getResource("maven-dep-test-B.zip"))); - - buildAndAssertSuccess(projA); - buildAndAssertSuccess(projB); - - projA.setScm(new ExtractResourceSCM(getClass().getResource("maven-dep-test-A-changed.zip"))); - buildAndAssertSuccess(projA); - - // at this point runB2 should be in the queue, so wait until that completes. - waitUntilNoActivityUpTo(90*1000); - assertEquals("Expected most recent build of second project to be #2", 2, projB.getLastBuild().getNumber()); - } - - /** - * Verifies dependency build ordering of multiple SNAPSHOT dependencies. - * Note - has to build the projects once each first in order to get dependency info. - * B depends on A, C depends on A and B. Build order should be A->B->C. - */ - public void testMixedTransitiveSnapshotTrigger() throws Exception { - configureDefaultMaven(); - - MavenModuleSet projA = createMavenProject("snap-dep-test-up"); - projA.setGoals("clean install"); - projA.setScm(new ExtractResourceSCM(getClass().getResource("maven-dep-test-A.zip"))); - - MavenModuleSet projB = createMavenProject("snap-dep-test-mid"); - projB.setGoals("clean install"); - projB.setIgnoreUpstremChanges(false); - projB.setQuietPeriod(0); - projB.setScm(new ExtractResourceSCM(getClass().getResource("maven-dep-test-B.zip"))); - - MavenModuleSet projC = createMavenProject("snap-dep-test-down"); - projC.setGoals("clean install"); - projC.setIgnoreUpstremChanges(false); - projC.setQuietPeriod(0); - projC.setScm(new ExtractResourceSCM(getClass().getResource("maven-dep-test-C.zip"))); - - buildAndAssertSuccess(projA); - buildAndAssertSuccess(projB); - buildAndAssertSuccess(projC); - - projA.setScm(new ExtractResourceSCM(getClass().getResource("maven-dep-test-A-changed.zip"))); - - buildAndAssertSuccess(projA); - - waitUntilNoActivityUpTo(90*1000); // wait until dependency build trickles down - assertEquals("Expected most recent build of second project to be #2", 2, projB.getLastBuild().getNumber()); - assertEquals("Expected most recent build of third project to be #2", 2, projC.getLastBuild().getNumber()); - } -} diff --git a/test/src/test/java/hudson/maven/ModuleDependencyLocator.java b/test/src/test/java/hudson/maven/ModuleDependencyLocator.java deleted file mode 100644 index 88f3620e4a..0000000000 --- a/test/src/test/java/hudson/maven/ModuleDependencyLocator.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi - * - * 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.maven; - -import hudson.ExtensionPoint; -import hudson.ExtensionList; -import hudson.Extension; -import jenkins.model.Jenkins; -import org.apache.maven.project.MavenProject; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -/** - * Extension point in Hudson to find additional dependencies from {@link MavenProject}. - * - *

    - * Maven plugin configurations often have additional configuration entries to specify - * artifacts that a build depends on. Plugins can contribute an implementation of - * this interface to find such hidden dependencies. - * - *

    - * To register implementations, put {@link Extension} on your subclass. - * - * @author Kohsuke Kawaguchi - * @since 1.264 - * @see HUDSON-2685 - */ -public abstract class ModuleDependencyLocator implements ExtensionPoint { - /** - * Discovers hidden dependencies. - * - * @param project - * In memory representation of Maven project, from which the hidden dependencies will be extracted. - * Never null. - * @param pomInfo - * Partially filled {@link PomInfo} object. Dependencies returned from this method will be - * added to this object by the caller. - */ - public abstract Collection find(MavenProject project, PomInfo pomInfo); - - /** - * Returns all the registered {@link ModuleDependencyLocator} descriptors. - */ - public static ExtensionList all() { - return Jenkins.getInstance().getExtensionList(ModuleDependencyLocator.class); - } - - /** - * Facade of {@link ModuleDependencyLocator}. - */ - /*package*/ static class ModuleDependencyLocatorFacade extends ModuleDependencyLocator { - @Override - public Collection find(MavenProject project, PomInfo pomInfo) { - Set r = new HashSet(); - for (ModuleDependencyLocator m : all()) - r.addAll(m.find(project,pomInfo)); - return r; - } - } -} diff --git a/test/src/test/java/hudson/maven/PlexusModuleContributorTest.java b/test/src/test/java/hudson/maven/PlexusModuleContributorTest.java deleted file mode 100644 index 9c893fe446..0000000000 --- a/test/src/test/java/hudson/maven/PlexusModuleContributorTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package hudson.maven; - -import hudson.FilePath; -import hudson.model.AbstractBuild; -import hudson.remoting.Which; -import hudson.slaves.DumbSlave; -import hudson.tasks.Maven.MavenInstallation; -import org.junit.Rule; -import org.junit.Test; -import org.jvnet.hudson.test.JenkinsRule; -import org.jvnet.hudson.test.SingleFileSCM; -import org.jvnet.hudson.test.TestExtension; -import test.BogusPlexusComponent; - -import java.io.File; -import java.io.IOException; - -/** - * @author Kohsuke Kawaguchi - */ -public class PlexusModuleContributorTest { - @Rule - public JenkinsRule j = new JenkinsRule(); - - - /** - * Tests the effect of PlexusModuleContributor by trying to parse a POM that uses a custom packaging - * that only exists inside our custom jar. - */ - @Test - public void testCustomPlexusComponent() throws Exception { - j.configureDefaultMaven("apache-maven-2.2.1", MavenInstallation.MAVEN_21); - MavenModuleSet p = j.createMavenProject(); - p.setScm(new SingleFileSCM("pom.xml",getClass().getResource("custom-plexus-component.pom"))); - p.setGoals("clean"); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); - } - - @Test - public void testCustomPlexusComponent_Maven3() throws Exception { - j.configureDefaultMaven("apache-maven-3.0.1", MavenInstallation.MAVEN_30); - MavenModuleSet p = j.createMavenProject(); - p.setScm(new SingleFileSCM("pom.xml",getClass().getResource("custom-plexus-component.pom"))); - p.setGoals("clean"); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); - } - - @Test - public void testCustomPlexusComponent_Maven3_slave() throws Exception { - j.configureDefaultMaven("apache-maven-3.0.1", MavenInstallation.MAVEN_30); - DumbSlave s = j.createSlave(); - s.toComputer().connect(false).get(); - - MavenModuleSet p = j.createMavenProject(); - p.setAssignedLabel(s.getSelfLabel()); - - p.setScm(new SingleFileSCM("pom.xml",getClass().getResource("custom-plexus-component.pom"))); - p.setGoals("clean"); - j.assertBuildStatusSuccess(p.scheduleBuild2(0)); - } - - @TestExtension - public static class PlexusLoader extends PlexusModuleContributorFactory { - @Override - public PlexusModuleContributor createFor(AbstractBuild context) throws IOException, InterruptedException { - File bogusPlexusJar = Which.jarFile(BogusPlexusComponent.class); - final FilePath localJar = context.getBuiltOn().getRootPath().child("cache/bogusPlexus.jar"); - localJar.copyFrom(new FilePath(bogusPlexusJar)); - - return PlexusModuleContributor.of(localJar); - } - } -} diff --git a/test/src/test/java/hudson/maven/RedeployPublisherTest.java b/test/src/test/java/hudson/maven/RedeployPublisherTest.java deleted file mode 100644 index d1d46e8e59..0000000000 --- a/test/src/test/java/hudson/maven/RedeployPublisherTest.java +++ /dev/null @@ -1,256 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi - * - * 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.maven; - -import hudson.model.Result; -import hudson.tasks.Maven.MavenInstallation; - -import org.apache.commons.lang.StringUtils; -import org.jvnet.hudson.test.Bug; -import org.jvnet.hudson.test.ExtractResourceSCM; -import org.jvnet.hudson.test.SingleFileSCM; -import org.jvnet.hudson.test.Email; - -import java.io.File; -import java.io.FilenameFilter; -import java.util.Arrays; -import static org.junit.Assert.*; -import org.junit.Assume; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.jvnet.hudson.test.JenkinsRule; - -/** - * @author Kohsuke Kawaguchi - */ -public class RedeployPublisherTest { - - @Rule public JenkinsRule j = new JenkinsRule(); - @Rule public TemporaryFolder tmp = new TemporaryFolder(); - - @Bug(2593) - @Test - public void testBug2593() throws Exception { - Assume.assumeFalse("Not a v4.0.0 POM. for project org.jvnet.maven-antrun-extended-plugin:maven-antrun-extended-plugin at /home/jenkins/.m2/repository/org/jvnet/maven-antrun-extended-plugin/maven-antrun-extended-plugin/1.39/maven-antrun-extended-plugin-1.39.pom", "https://jenkins.ci.cloudbees.com/job/core/job/jenkins_main_trunk/".equals(System.getenv("JOB_URL"))); - j.configureDefaultMaven(); - MavenModuleSet m2 = j.createMavenProject(); - File repo = tmp.getRoot(); - - // a fake build - m2.setScm(new SingleFileSCM("pom.xml",getClass().getResource("big-artifact.pom"))); - m2.getPublishersList().add(new RedeployPublisher("",repo.toURI().toString(),true, false)); - - MavenModuleSetBuild b = m2.scheduleBuild2(0).get(); - j.assertBuildStatus(Result.SUCCESS, b); - - // TODO: confirm that the artifacts use a consistent timestamp - // TODO: we need to somehow introduce a large delay between deploy since timestamp is only second precision - // TODO: or maybe we could use a btrace like capability to count the # of invocations? - - System.out.println(repo); - } - - @Test - public void testConfigRoundtrip() throws Exception { - MavenModuleSet p = j.createMavenProject(); - RedeployPublisher rp = new RedeployPublisher("theId", "http://some.url/", true, true); - p.getPublishersList().add(rp); - j.submit(j.new WebClient().getPage(p,"configure").getFormByName("config")); - j.assertEqualBeans(rp,p.getPublishersList().get(RedeployPublisher.class),"id,url,uniqueVersion,evenIfUnstable"); - } - -// /** -// * Makes sure that the webdav wagon component we bundle is compatible. -// */ -// public void testWebDavDeployment() throws Exception { -// configureDefaultMaven(); -// MavenModuleSet m2 = createMavenProject(); -// -// // a fake build -// m2.setScm(new SingleFileSCM("pom.xml",getClass().getResource("big-artifact.pom"))); -// m2.getPublishersList().add(new RedeployPublisher("","dav:http://localhost/dav/",true)); -// -// MavenModuleSetBuild b = m2.scheduleBuild2(0).get(); -// assertBuildStatus(Result.SUCCESS, b); -// } - - /** - * Are we having a problem in handling file names with multiple extensions, like ".tar.gz"? - */ - @Email("http://www.nabble.com/tar.gz-becomes-.gz-after-Hudson-deployment-td25391364.html") - @Bug(3814) - @Test - public void testTarGz() throws Exception { - j.configureDefaultMaven(); - MavenModuleSet m2 = j.createMavenProject(); - File repo = tmp.getRoot(); - - // a fake build - m2.setScm(new SingleFileSCM("pom.xml",getClass().getResource("targz-artifact.pom"))); - m2.getPublishersList().add(new RedeployPublisher("",repo.toURI().toString(),false, false)); - - MavenModuleSetBuild b = m2.scheduleBuild2(0).get(); - j.assertBuildStatus(Result.SUCCESS, b); - - assertTrue("tar.gz doesn't exist",new File(repo,"test/test/0.1-SNAPSHOT/test-0.1-SNAPSHOT-bin.tar.gz").exists()); - } - - @Test - public void testTarGzUniqueVersionTrue() throws Exception { - j.configureDefaultMaven(); - MavenModuleSet m2 = j.createMavenProject(); - File repo = tmp.getRoot(); - - // a fake build - m2.setScm(new SingleFileSCM("pom.xml",getClass().getResource("targz-artifact.pom"))); - m2.getPublishersList().add(new RedeployPublisher("",repo.toURI().toString(),true, false)); - - MavenModuleSetBuild b = m2.scheduleBuild2(0).get(); - j.assertBuildStatus(Result.SUCCESS, b); - - File artifactDir = new File(repo,"test/test/0.1-SNAPSHOT/"); - String[] files = artifactDir.list( new FilenameFilter() - { - - public boolean accept( File dir, String name ) - { - System.out.print( "deployed file " + name ); - return name.contains( "-bin.tar.gz" ) || name.endsWith( ".jar" ) || name.endsWith( "-bin.zip" ); - } - }); - System.out.println("deployed files " + Arrays.asList( files )); - assertFalse("tar.gz doesn't exist",new File(repo,"test/test/0.1-SNAPSHOT/test-0.1-SNAPSHOT-bin.tar.gz").exists()); - assertTrue("tar.gz doesn't exist",!files[0].contains( "SNAPSHOT" )); - for (String file : files) { - if (file.endsWith( "-bin.tar.gz" )) { - String ver = StringUtils.remove( file, "-bin.tar.gz" ); - ver = ver.substring( ver.length() - 1, ver.length() ); - assertEquals("-bin.tar.gz not ended with 1 , file " + file , "1", ver); - } - if (file.endsWith( ".jar" )) { - String ver = StringUtils.remove( file, ".jar" ); - ver = ver.substring( ver.length() - 1, ver.length() ); - assertEquals(".jar not ended with 1 , file " + file , "1", ver); - } - if (file.endsWith( "-bin.zip" )) { - String ver = StringUtils.remove( file, "-bin.zip" ); - ver = ver.substring( ver.length() - 1, ver.length() ); - assertEquals("-bin.zip not ended with 1 , file " + file , "1", ver); - } - } - - } - - @Test - public void testTarGzMaven3() throws Exception { - - MavenModuleSet m3 = j.createMavenProject(); - MavenInstallation mvn = j.configureMaven3(); - m3.setMaven( mvn.getName() ); - File repo = tmp.getRoot(); - // a fake build - m3.setScm(new SingleFileSCM("pom.xml",getClass().getResource("targz-artifact.pom"))); - m3.getPublishersList().add(new RedeployPublisher("",repo.toURI().toString(),false, false)); - - MavenModuleSetBuild b = m3.scheduleBuild2(0).get(); - j.assertBuildStatus(Result.SUCCESS, b); - - assertTrue( MavenUtil.maven3orLater( b.getMavenVersionUsed() ) ); - File artifactDir = new File(repo,"test/test/0.1-SNAPSHOT/"); - String[] files = artifactDir.list( new FilenameFilter() - { - - public boolean accept( File dir, String name ) - { - return name.endsWith( "tar.gz" ); - } - }); - assertFalse("tar.gz doesn't exist",new File(repo,"test/test/0.1-SNAPSHOT/test-0.1-SNAPSHOT-bin.tar.gz").exists()); - assertTrue("tar.gz doesn't exist",!files[0].contains( "SNAPSHOT" )); - } - - @Test - public void testTarGzUniqueVersionTrueMaven3() throws Exception { - MavenModuleSet m3 = j.createMavenProject(); - MavenInstallation mvn = j.configureMaven3(); - m3.setMaven( mvn.getName() ); - File repo = tmp.getRoot(); - // a fake build - m3.setScm(new SingleFileSCM("pom.xml",getClass().getResource("targz-artifact.pom"))); - m3.getPublishersList().add(new RedeployPublisher("",repo.toURI().toString(),true, false)); - - MavenModuleSetBuild b = m3.scheduleBuild2(0).get(); - j.assertBuildStatus(Result.SUCCESS, b); - - assertTrue( MavenUtil.maven3orLater( b.getMavenVersionUsed() ) ); - - File artifactDir = new File(repo,"test/test/0.1-SNAPSHOT/"); - String[] files = artifactDir.list( new FilenameFilter() - { - - public boolean accept( File dir, String name ) - { - return name.contains( "-bin.tar.gz" ) || name.endsWith( ".jar" ) || name.endsWith( "-bin.zip" ); - } - }); - System.out.println("deployed files " + Arrays.asList( files )); - assertFalse("tar.gz doesn't exist",new File(repo,"test/test/0.1-SNAPSHOT/test-0.1-SNAPSHOT-bin.tar.gz").exists()); - assertTrue("tar.gz doesn't exist",!files[0].contains( "SNAPSHOT" )); - for (String file : files) { - if (file.endsWith( "-bin.tar.gz" )) { - String ver = StringUtils.remove( file, "-bin.tar.gz" ); - ver = ver.substring( ver.length() - 1, ver.length() ); - assertEquals("-bin.tar.gz not ended with 1 , file " + file , "1", ver); - } - if (file.endsWith( ".jar" )) { - String ver = StringUtils.remove( file, ".jar" ); - ver = ver.substring( ver.length() - 1, ver.length() ); - assertEquals(".jar not ended with 1 , file " + file , "1", ver); - } - if (file.endsWith( "-bin.zip" )) { - String ver = StringUtils.remove( file, "-bin.zip" ); - ver = ver.substring( ver.length() - 1, ver.length() ); - assertEquals("-bin.zip not ended with 1 , file " + file , "1", ver); - } - } - } - - @Bug(3773) - @Test - public void testDeployUnstable() throws Exception { - j.configureDefaultMaven(); - MavenModuleSet m2 = j.createMavenProject(); - File repo = tmp.getRoot(); - // a build with a failing unit tests - m2.setScm(new ExtractResourceSCM(getClass().getResource("maven-test-failure-findbugs.zip"))); - m2.getPublishersList().add(new RedeployPublisher("",repo.toURI().toString(),false, true)); - - MavenModuleSetBuild b = m2.scheduleBuild2(0).get(); - j.assertBuildStatus(Result.UNSTABLE, b); - - assertTrue("Artifact should have been published even when the build is unstable", - new File(repo,"test/test/1.0-SNAPSHOT/test-1.0-SNAPSHOT.jar").exists()); - } -} diff --git a/test/src/test/java/hudson/maven/ResultExposingBuilder.java b/test/src/test/java/hudson/maven/ResultExposingBuilder.java deleted file mode 100644 index 2bc74dd15d..0000000000 --- a/test/src/test/java/hudson/maven/ResultExposingBuilder.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2013, Dominik Bartholdi - * - * 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.maven; - -import hudson.Launcher; -import hudson.model.BuildListener; -import hudson.model.Result; -import hudson.model.AbstractBuild; -import hudson.tasks.Builder; - -import java.io.IOException; - -/** - * @author Dominik Bartholdi (imod) - */ -public class ResultExposingBuilder extends Builder { - private Result result; - - public ResultExposingBuilder() { - } - - @Override - public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { - result = build.getResult(); - return super.perform(build, launcher, listener); - } - - public Result getResult() { - return result; - } -} \ No newline at end of file diff --git a/test/src/test/java/hudson/maven/reporters/MavenMailerTest.java b/test/src/test/java/hudson/maven/reporters/MavenMailerTest.java deleted file mode 100644 index 3c1c727e12..0000000000 --- a/test/src/test/java/hudson/maven/reporters/MavenMailerTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2011, Dominik Bartholdi - * - * 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.maven.reporters; - -import hudson.maven.MavenModuleSet; -import hudson.model.Result; -import hudson.tasks.Mailer; -import hudson.tasks.Mailer.DescriptorImpl; - -import javax.mail.Address; -import javax.mail.internet.InternetAddress; - -import jenkins.model.Jenkins; - -import org.jvnet.hudson.test.Bug; -import org.jvnet.hudson.test.ExtractResourceSCM; -import org.jvnet.hudson.test.HudsonTestCase; -import org.jvnet.mock_javamail.Mailbox; - -/** - * - * @author imod (Dominik Bartholdi) - * - */ -public class MavenMailerTest extends HudsonTestCase { - - @Bug(5695) - public void testMulipleMails() throws Exception { - - // there is one module failing in the build, therefore we expect one mail for the failed module and one for the over all build status - final Mailbox inbox = runMailTest(true); - assertEquals(2, inbox.size()); - - } - - @Bug(5695) - public void testSingleMails() throws Exception { - - final Mailbox inbox = runMailTest(false); - assertEquals(1, inbox.size()); - - } - - public Mailbox runMailTest(boolean perModuleEamil) throws Exception { - - final DescriptorImpl mailDesc = Jenkins.getInstance().getDescriptorByType(Mailer.DescriptorImpl.class); - - // intentionally give the whole thin in a double quote - Mailer.descriptor().setAdminAddress("\"me \""); - - String recipient = "you "; - Mailbox yourInbox = Mailbox.get(new InternetAddress(recipient)); - yourInbox.clear(); - - configureDefaultMaven(); - MavenModuleSet mms = createMavenProject(); - mms.setGoals("test"); - mms.setScm(new ExtractResourceSCM(getClass().getResource("/hudson/maven/maven-multimodule-unit-failure.zip"))); - assertBuildStatus(Result.UNSTABLE, mms.scheduleBuild2(0).get()); - - MavenMailer m = new MavenMailer(); - m.recipients = recipient; - m.perModuleEmail = perModuleEamil; - mms.getReporters().add(m); - - mms.scheduleBuild2(0).get(); - - Address[] senders = yourInbox.get(0).getFrom(); - assertEquals(1, senders.length); - assertEquals("me ", senders[0].toString()); - - return yourInbox; - } - -} diff --git a/test/src/test/java/hudson/maven/reporters/MavenSiteArchiverTest.java b/test/src/test/java/hudson/maven/reporters/MavenSiteArchiverTest.java deleted file mode 100644 index 359fb4913e..0000000000 --- a/test/src/test/java/hudson/maven/reporters/MavenSiteArchiverTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package hudson.maven.reporters; - -import org.jvnet.hudson.test.HudsonTestCase; -import hudson.maven.MavenProjectTest; - -/** - * @author Kohsuke Kawaguchi - */ -public class MavenSiteArchiverTest extends HudsonTestCase { - /** - * Makes sure that the site archiving happens automatically. - * The actual test resides in {@link MavenProjectTest#testSiteBuild()} - */ - public void testSiteArchiving() throws Exception { - } -} diff --git a/test/src/test/java/hudson/maven/reporters/SurefireArchiverTest.java b/test/src/test/java/hudson/maven/reporters/SurefireArchiverTest.java deleted file mode 100644 index 7e3ae538ee..0000000000 --- a/test/src/test/java/hudson/maven/reporters/SurefireArchiverTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2010, InfraDNA, 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.maven.reporters; - -import hudson.maven.MavenBuild; -import hudson.maven.MavenModuleSet; -import hudson.maven.MavenModuleSetBuild; -import hudson.maven.MavenProjectActionBuilder; -import hudson.maven.reporters.SurefireArchiver.FactoryImpl; -import hudson.model.Result; -import org.jvnet.hudson.test.ExtractResourceSCM; -import org.jvnet.hudson.test.HudsonTestCase; - -/** - * @author Kohsuke Kawaguchi - */ -public class SurefireArchiverTest extends HudsonTestCase { - public void testSerialization() throws Exception { - configureDefaultMaven(); - MavenModuleSet m = createMavenProject(); - m.setScm(new ExtractResourceSCM(getClass().getResource("../maven-surefire-unstable.zip"))); - m.setGoals("install"); - - MavenModuleSetBuild b = m.scheduleBuild2(0).get(); - assertBuildStatus(Result.UNSTABLE, b); - - - MavenBuild mb = b.getModuleLastBuilds().values().iterator().next(); - boolean foundFactory=false,foundSurefire=false; - for (MavenProjectActionBuilder x : mb.getProjectActionBuilders()) { - if (x instanceof FactoryImpl) - foundFactory = true; - if (x instanceof SurefireArchiver) - foundSurefire = true; - } - - assertTrue(foundFactory); - assertFalse(foundSurefire); - } - -} diff --git a/test/src/test/resources/hudson/maven/HUDSON-4192.zip b/test/src/test/resources/hudson/maven/HUDSON-4192.zip deleted file mode 100644 index 24e286e883e6dadfd1ad92dd5e44fec0e9486672..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2117 zcmWIWW@Zs#U|`^2Ff8S<-lK5%gFGVxLm?9b13QB#LqUG7UPW$BXb2|*Gp~8GmnIO0 zRP) zhPJiWZ55^*I)8ZI483hC4-Z8sX>^+hn%q6n)Bb&u(CMW=+G9)nA2u$zKlh?!HG)7*vnm{=Blna$oWd_G3+3aw6aK^Sv&aeE(&~Lgl$DQo1@Pah{r4=)m#! zjKhW1?^xo+KbKrc4P0}(@}lWGF`K56srN3od6lj;nE%|QKvsQ5?W30~pXE=>uVV}V z1}qmaU>U#x3uZtG7GS^@7bWXs4OQIQIHB4~Qj1GS)ys#hH!HC$kyHbOF$}=(3`CMz zR8zW#516DXfJsUoyD5$Z1tCCB>46-EHBf2;5BeQ8;Mx0JyXKA7#d|kXGs^jLY8-G{0!kybG#*Zse8f4ITVQ9QL&c|kNgKlKWZ5n|7{8GCoGr${8uP<5 z<2J|U<-aDS2c8W*WVS$U-t*i!oU_hIZJfF3BwP($*k~w0=VLaNXj1KXv}G z@%kT5*fC&(a5)PRwx~r4a@YoAHx0FbA<~_wfwq|$;!bGRBGL@hKzomD1|@-J nz=Gr$A_EOIlOYFM8FteUSq{astZbkJ%mswcfr0J83gQ6(!{}fZ diff --git a/test/src/test/resources/hudson/maven/JENKINS-1557.zip b/test/src/test/resources/hudson/maven/JENKINS-1557.zip deleted file mode 100644 index 95a102f9f39f5b676e1e80dfe57dd730b8910ea0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3420 zcmWIWW@Zs#U|`^2;E&C+UdL>5qm_w)!IYhWft^8yp&&n3uOc@mG=!6ZS*=9z9UtcV+UM2; zd=h?p!CjJf!rS%n%k+b5OpdC2*L~q98erROHqSPnL&Ng0Q_#w(tKWCcSuLv8$8hlZxd9G8b|$v;Ul@@#PT5TC)X=hX2=2y?ml+SxzGC**Uj1DH0{K%T}v8mIrwHwDw$?* zd$n-r;v=kUwZnhqn)^L>eXg?Oe)RT3JI-%=k;Ax9VWQ=`o9kDJiN4SY;}I9kvO4>` zPWr3GzEV>FR@~!d&{SGg)VnoH2?USV{e2{ zRzBNtCfP|n$XoT{+ylu%{hc-we!fed>ptb5$LnR^cW&WKa`UTsZer;5EB&B-o#_cC z74h8b!k?~vDoL-GzBl_|s7Jn&wEVn3{~p9Yod5ov?bdwLC(+CP%JPM=@P^;(``uGE z=i#60LiYdm$Q$ju{nI`Gn2Nc8shEKwwJ6IP%zzRsz*JmZl&lXR- zxOHjmDN=eE$ zBGW(_+y*9NCJ%8u*z*OeW$iQGmoZ-Qlxdl0? zp?R4lA;1Ks2Z}&&x;)_Y(+h;r(&gITjeJc80&V3&_a3^%W<7e9awqlxyT)5i(HEwB z!&HuxZ&%%6x$92R{qOau50|`3?Ou=}vTNbujHJ_xx@Vo7cWRN&nHMM6D_pXhc2rwF za@i+tTC~IC*wn&}l08hO9%(Bk89vZ`IFqr4b<3=W+Eb2B5^eCUnsia%(r2%Q#@p7) zXeqbfwKMj%YiGVP_n|1$x!1-!LY&%8w)7=2_?~3Rd|s;L{r>*M&Ay^vqF?JzEU^P+ zBS`S{0E1_*o>k!{VDPvBgU6VZ;PFHa8x3H%fG|eb?Cv{cAmUQ({jXhfp`noO)vm1B zxewT7e&;25)U1t}-G1TU?^}W;Q%o+OnQ3`)*XFmJed>>`@6B5GC8_OE>tjFdwTGru zoU@oJvFYdBw=UuH&Oh^*^gS#iG3e#72M^q|o19~NzT9QFpd0+9Nqp^9XYB_k<1H$l zMExz0b9%$y8MEPW*4-JbEN0R&?F!s%nb*sgtWrE^`lQjj?fI_O-tSMBoc$NIN4WNA z)}o&;e|LDSmikY)l*vuZB%A=T7jv*u25bbiN*SNH zC*3$<6yw~HjYEzK(hcTAF?b5H!O&5#fI|)Y=Bo5_$|%O8rOL&zA9ZX z+hUu^?=M$xrL})-lyy|PX7G>8<0Y5<|B6G=Tgn2%JpWfrzk9Re;M)?bg_p(OZ-`u9 z{O9@0k4IfCHu!iX&wL`VTY97Mye^{$GJ^KCfhi>=$?bkS|KTL`Ho~k`_1+TV!teFLKzW&}>tY_H{ znNReRlW5z;_di(U`g-2CsQvIWo>;i+&gPxp zJ=|3+tR^*xTxig_?IQKc1}&II>D@I_u@cwR8u13HjVS*$NYK1c`q!; zs_2)R6DFnT?5(}r`G@M|{{g@xzy(YK3=C&ulAXW|D8T|u0>wqi`r!Ci)G!XL$Bci3 z?n!96Iib396EpK5Mr>9$4txQ`Xh{gS5q!u-WF?j*BFs6A-5f|-207)WTwGv0&?ALF zEQMi?YeiyiK~Ab3$WXBNKyJfg=3^}l-%}@j!aUA->ga_Pto1mfd%`nx#j_v=rWGND zo=Kb&v`(M*2|VF@=FDXdJSD{8LPe&i zJxx*5R(Y->Cazu4){r6+r0o^-IjBoC;gra#ojXJt7*=i5u{UPTX>3yqaAV{;&;yMY zMkYCCT;-w!IFNzGB*T_Q5EEWXvO-EpG&cu$gABu!!ytw+FbFXGb!28@faW)#Nud0O z$0W?$hHR1})Ff!$0~!ZP-y|5v$AD~H3$k(0l!VVrT)7ZA8w|n*>Pr& zwJ!R9Pc`*o(8NX8<9G_o%F3qgHrZ!)O6_*>>z1(9HDO_u$3!o04Y(cp@5hIQ5l^nO zNUx5!{46Md?*9LErv2>|Y)q>E8e$vz^x{>^ohOWRO3%*D zuRX59m0)s^DKon?+wN*hX5=ZpI^Iy(4C$V>fCYiCk8-EV^=;Bls$gL~_A=|_o|^a^ zyZd+Y?e5toY>}TD6jHQ_FKn~^+64Pez3jbjjyXy1GnQF=g5%08>2+B%`XA^qB(Heg zIPv@SFi!iA(ub9s)sI~%QsHIOKkaHc|3`1)@huOJ6tmy_TmGKS_ifKP_2Y??dD^R1 zGU}_G2*^CKd;VXR`Oz1zq#mkIEP3W~aUz=ri^j4!EV|{pw&rLUII$_j%-*o!{H?Mh zk!;;cB3_jbtX$7a>M#0u=g?+_d)o}Ae6G}JURHYU`M%;QK2D|2*H+{mZd$)`QMBI9 zPbc?Z^y^uj95|Wnp7Z9*=foBDRvz-0b6nJ1C1{g~+%`3#sM`}R8+&@4cHKHd@o4{A zXCG~Tu>dW;>D=vY!R+!9t$(guy&_-MCAIzXzt{E2H3eVVKX31CE8K6%7qNCn_QU!8 zF@M+sfC-BWn6MZa&aNx412dom3ov077bWY1Q&p34qwhQ*MoU!)-S5zJb3%2Oq!yPz zjMxv1WZjVC?IGuq;vrJ{l{*1OG!oshE`w+p+khpjs*oFz*y1)1u;CbK@pA?*|mWO z{SF)O?0v3X^G0jpy_cyO<$O6c4%c?6=J+l;lIWB1zi;w3o?at)nUn8-?hU`b^4Wr_ zmScQH7RBYK<}58*C+5U|uBz#KhRMlus~P>T$v@#^zWV(@FIUC%4ZX8wD6ZNh5^zY{ zxS@h??pJ||j~ySL%t_fGr1!3WSL3r4P9B$47?^f^n0#RiW19cRsmCKtLpi1Uy98eF zy8l(%=$5l=&D=*hl6B8|5`s40edIUwzUBpyr4723xxZE)(%O7XXQflgi|+V}xeL2@ z`0oDZadCEz?4`~kzBuj;$8R#-UTV-N@^NzhB9pfr6#nz4Dd``FW!(4sDf+v% zU%YK`PfAWc%v;fJOMW?2t143Zo`h9z!$EG7jsQ3Z&l zu^R@;qvE@zvGyJfpkfsTNSgNtBTR;db<#2g|`=x1;9Z z&G^Cq-5gjBMwo+=gGn+AmV;5vqE-&ZWh!Yo_;%KeV?Ds=+5*HH$WaCkLy+GQ?gg0) z%)tb6uWs)~zGed+m-70fQ&$?a@F%>wHg}=((FbfTi`0%NEfih#4ov@iJeKls_2 zZ(Zx$6U^sce0J0K@m}Wh$xb^DyFXc8=3B|3rgyWW=w|jJi>mzU{Kic;U-p{)VhZqP zWRhdX)t-?6`kH|OSb#AsX#}z0ts7QI>jte@f*6LY4ucrRz#zcz)-e)TAVO+1ph;K? zIAoJB>oa7NrbA7F)^0%KK(!kQ4&!4$HtrU(anO81*om0A1ldd_V9O8WS+En~=_WLU zbaQ1;+?t2gTts>##|8xy8;%gL0g{*S`2$y72nioZ%93Nj7Cs1nK=Tz+&4|xNNbEqU{(dl#yvnb4!L3gx)W4}V>K98wSXKlx@<`9L>P>y zB=A{)Js+Yb_c_QGP${`T!7vk2MG%fO^lXHh+zr@~0t23n5Xpq{D1*vU0fz0s)OU^p!~+1>SwWuw diff --git a/test/src/test/resources/hudson/maven/JENKINS-9326.zip b/test/src/test/resources/hudson/maven/JENKINS-9326.zip deleted file mode 100644 index a9b50b23ae9b516ccdf022681c0d75665101bf9a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12849 zcmds7cRZEh+ZV~+BP%m293!$aGmnw%k+S#RC6sZHjFe4e?~sv@tYl5&d_Jc`p6h$x*Y$kw>$<3p247| z4&Nj9hCl5r2Ct&ioHAyPSfvL`Dy1|%BiU9UdHidviLMrm1I-yOh!Mrz-O zY`}Ai$sHyfz@sW$6coB6d~kBK2YK1sebgRz?B^l$l^_1DrsZ8U=0PH@5vz#F!eEbD zddn-Rb}EsISaeX;&uaL4SBpgSQ&V#ibM7hj&27=~G0$hsZA1BIFmTiom7GPAqgI?y z81D(6Y?)i`*w~(Zwah8z6*B<#4erE^0qSH(j@h^l373(9iIvmu48bN7+P}` z1bSo}kQMr`k3vq>+IuCimIv*8GQ*=NW zBWf;QnZv%aeHy(XYJL(6X_7N+(LMs;R~RH`&$wVnsXg(+b*A{jB1> zi5+TH&o}KSF6Qf$V2Fr5c)&B@&{*flukosG@oFjnX2pj_pN@5S0iA|p~8cH-ln^`wH~^2`}h z#%sHK--$cSYcJ**q3Z>-Y#DoncshMIcYg1~*Bi_{Al@paj6--+`WEYCv+j+g%L>|0 zCkQ4M6}T!FRlUE{hL!n+d^-6l*>q%YvC8s9V5K|7>ogF)S6kzPnUP9oXJ)=!l4)rw zmFJxjR4ygAQi(L9X3{H|c@t0Z)~dgwp;y!D`MgULouIR-;r%=oW8;`J!;I}@H^vtK z-fwfs^_>ru?j<(1<+C4>Muxf_EqN;HYXif;o=eS}R}arPCy}lrH*;4vxCBz=5-qL> z-mUBcZgxI~g2HgP5y;fe*wxj^*v%>?N<|5cmk`qZMTfK6^Uam!CWo+>*yM$M#aEbB zAd6PX&5G&^^;N0m>uR(17GBfuKNiXPNfhg*%dB25I5wJ(I^?A^`ubCy&!_Q%-Vzq-D0su#&s%$ZeVK^Lk^lK>$?)IYklt|V3`OSjO+TJ!e)RVgrFXliGD=zAeq)hcY}dY zyy*VI-u_NyeVSp+|F)oPuDE2P^4t5bPAiwSw0f4+WQB2Msm-#F3$Cg7PDSeP?RY#x z1A`{guagqIrs+98U!tcue${dF;_I>pc?!ljEcMcSQzp|+Vl^7MVgt9RO0kTK6wMU$ z+SIuWNop}VE21+dCw((C%aaq`XY^}s8x7w19MsA`tR7$Tyd1TbbmHCvLV&}t8``=22RQ&hAiy_K z;QMDSgq20meu#gWL;qrKYG>`_Y7Vk7a|4+=x|jnGz}&($CPE6kk%v@f^8w?8I0X^e zLmaY`*#2oJLl(h+Z9VGwH;v z@MOQhnvTvOQ09AcNH(vNt*n2%wTS&`gbW>nFu|8BDU4p#S`r#Q!EXOeU8#lF8I&(i zcHn=UvbScsV7~s9l_J`+GVYX<>YX~Jo^zHZ3)BkEMQ9vRchQVrihT{8_L`AjM*p2+ z6{LxIc7Werz~`U+cQP}F^Pj5?>OUcRkB=(vgt$;v8tGF)XLii%$HME0DOsGe*=er^ zK3g0~4(oeJwesKzoe1sDsZ$rW-#lF{Vj`f-W98q9K3zTg@*&vUyiWHT(<`EJ z${jkooEjnjhA_?$$i)tbe1DEn-4Pt1Kl~x8$ZAS)DXPlBQD78cP_Ue+bP`~N0#JbC zFRWFhR2Ai9H8epga$2umVf#yxqEFOaWcY0PGK5?%NisBzoN`gVu08Ob(wcmoPeA|r zWASm}88)>qX?QjMebFTfuFn)a$ZLpnaV3WMSXgvzh{Hv;~`7uX%CZiGfNkx)tsC%&jeul&1k;fL$^F<+YXa43 zzwWrJsa$N7B=iYWDAmpjp`-VmkX<$+c|<{M(ySB^N=O`uQ~c!k@SwxDt5=$>CJC11 zDJ^k^Wj;9YIa+v3zgpPcy5WQ2bo&AtJ0zM8H$`-OY3=zXMom?YFEvwC3TFqI_Fks& zZbG`W{7#>^&EU!+mPdt)^IAWW!RPZhHVKYySxE@)g|3(fR`U0rVLq*uAjiG-mTuWO z5<|Up%}jr+BkJM8UcFR`v0e@+$Yu>|gY*&8#kSDdHZ@|!{rtKYH;dchmUhq5 z?mHz0rhTRv3w*oxUMz%z18#3YjKB)szh>4CR=B!byP3nK03iVGUBnfA9)KI4fRE&V z0|(`X1{8RoAVF#!;0wHjVsjd{_O?C`G1Un#ojftBeVpA*i_|U;N2mJjVD>0Wl>GOO zn2AP$<&5UGjZ$_KRLd?i7FO1^y_KC46R4tEq!jq!Da)*;#KzTeU9KnZ$K8-|qtGK` zhP+YcJl-AnV0pxhMWb7-T2p_U12Wk*7yoeJY-AaPQG1>(qp^pEjmxp|{9TXJ(aL<~ z7%XH%y1Ha*OHI+KXIgtGznW%rs8J^xX$zO=ZSSf2MBROjAw@`vF0rg) zlIY%hj$LtBrTP13`)Gl&?1_=Cw(8B~WVb1k{sG1A3L-G5kFvc#P$?VCP$0Xr?iG`g zbFziVy5jmwCreta#ogjU`xy$s@)_dZ*fKJCu+G-rXY$mCG?iW-ZoxHv?kYh7p1~ev1=BpU^I~A)_;_mhaW*-w=AB@})!;@FJ-;uB_0v|S?d2#qeYw$Z zyZ8En3?!HDyQat5fEJfYP+n_uvOj)n{{EUj6B&PuwjmDYjb8OU!F()_(cIalorEe} z9vYh~P43>l7Z>v-F5Puc3f(; z2de{%c1!KpZW`GQY;?;CE4*%qn4W>Br2qVv!O-BSk+ADQ&TJ+Go=-*g%2MsJ_=07( z>)sCfZte-k ziySDtQ<$$>MVXV;V)1lV%Ii&yx8Tcf>!q19Ki$aBZ+eBx({vKH7y5_Vfy`lKwc~Mb z8r@4EZpJ{CkodRw?Zd%p2Q-N85lS{_yttB^C&r~ZNN>B|w8IXfP2+MaN?<$dAQoLZ zCCygteadoJUAmTVwZFf$=FSHU)#K{fPK1;t$C)b91gI8k*FYe>+lE*d#r*8&KBul- z5!PpmNdS8ledVp3u^h?j-tgYD^H8M!~>3gO~L*$b!WE|6kEV<3P#itNsq~KLFT-QX!AS&(3IF zbOSaj1F7uW4Km){at|&$|Jxq2YCbpTZ24Z~XzMJ=PNGY!Jte^VdhWU5 zCFcBkz2ViiO003lqT^*#x%d-J>yFLgLjKbdYbMK2CIhi!0yOQ{IlK(DgBrE!D62Pi zX8pYIZqSCVMwqUT2|h)Itw?B8fI$4Z4q?YH{rx+y(x2QHIFRq>0F0lh5=glRU2V`W zOXz?8m2xLSQ0|K8j*jSt27*mIodJSkvX_!%%(dG&-&j;U=pJzJtIprJbmltJ2smIfSvpPu5yDrDi1ioR6pKbRzcHkv#3RQu3K zpB$up_TxpK(@P8VqLaxK7;$O#T}HzEQIq-@+Vo8FjZxx&avxk{+WHZ+{Wdi;X!a#u z76;1xUtB**w7cC7Q=gS?&Z3`1=idXH#WyEpd)*+~sdV%NfrD+D(N=t?j2A>j9uy0H z68$OKJE5YT3=r+j2cn&eXdHcqE_M2cXfOOF+H2rSFXBT-DoMGK=yj-J5^jh|A@Hmj zfrN{+dcq9hq`F~515~QZZu$m2@$B&@mkUD;@RueYmTm7>$_-2P0w@=(@MI{B1BB zY^A2KhLzM;uU{Vti3F8!c)3HaY}Ju9KVEL@_h1WB$C1`^%|qSAq?+v67-|<85Ip@+ zE-F^-24^Yz@s7MIU-}qZF&hV(RIY>>T}RmJP(oN8SLW2zZofgCxsuYE|-8da#8r~ zOy^ejcxwGIJClAX|A(AfZ^!SIk+6N&%59NK%W{b_Vf&1MacdkWz4&|Oa|_dx(j~X}^eG_Q4bp>niDqHtre0y3aGSVh;8r=511+XH?XDvFI{(o>{x- z^`q=qvtxiKVDy*ij4K( z>Fe21*=A#qFp)Q>3W-|Qv_e`<&p)I=bA25aqO^D!yFhR}!Ty*E(;F8_d#jFz*xRu@ zB#EhaOq-_YBQm436Ju?MmFdpPimRk7(4^cLrd*t>An_V+Q#5*D2LXk1MQz9(V@sFu zCdbjG2`HFu$viK;JwmyV+`?kj8`&VRS{xZRUNX3;F#NWChv0ss)BMR;i3bx6y!oxU zDj_}KXfDE%5I*p^xjsxGMzcCRvm*TO{?gnp&#qX@1!adCdh2CBZ@2Et1AkD77O#l6 z{Eh<4_SOKsy7y}6z5uj>9u9E*4ZZd^sQcUkjv7@PB}s$2v$BfN(XqKF@RiF$4en5r zXybu+7INGRFMwGoF;bOuSU@*W=-Rz@Ntk)UyriQ%30t4=eKV8NQkcn}r9?9-I zA5tC_$kv;{_NFm6-!Zt@N1Hp)*=2ntd19sy)DmldZ;o@jyQEAmX8Zi}5LbRehRbs) zZBx=(w%)HI3T`(DhXiQlGzqG@ zHW&nFG{z12I))FN4fZAmm5N?p9X?2^6@WMFEhLsAbf!C8 zH7vQd(u(w=*trx=$PK9{*+^Zvs=bY-cN@#|o$t-tlzl9mFL~tzEer2*QJhK9luvu3 zW4Bvwd{*BmPNu6{>grtayp`v;tEVil`BKux*(-Kt#VR3vZ1neV3Em|HKP#yO%i(^B zyfs88!?)^D$>D)6791Voi|J^GwPA4m$*h5s$2?yAJz8bubivHX9eS-BoU-}O=kx_H ze_U*P7(hAOFkH8uY*>5OCfK;2bb?X-`Gs%}D`Soim(~p{x0-+8u5uSqS-5^`uzZryW}Yf}&-6lHPLzOCX|Jn}w}Uhv+ISkt0@Dk-;N8Mdgb)#3r$%Va%}CX+z1Ys4k2ez1Clt@{$B27jfCiI|nI+MW)9`{J zeok99BP=Ufk}plNvIW(gIBKhR#M3zCvp9;ZDYBftz7m+BGHi~R@{Hr|xLGSBwtm55PP!}RV57G!v z%N$oX;u{^4^ZBsJN`O6duePbvSJ>rTGgX=!Nq{VMp7!OdLDYnP^WUs6IF#j>A7Bc%^FRwsnu}?*0udnQ*(BI6w6?O#5!TIa{oc7U>y+{H>y9gw| zKYaQ*r=O`0-a*6wK~K~7FKATbjw1v6+3A7#hwOBH9|Y=O=iwkQWe)Ekvh!&e+z|Sq zbNW46`?NuJkPCD0+lj-$eyKaexgj3x!sd1!<1gS27mJ7kA)cVZ4TSlR1N|<|;2D4z zAo~4LfPbM3IEHoTKyVrgMg?T2p0H_4`~@1Eu!5f^;)x+t*X>`>X)<9SI?a6r14{{r zK+qN|4CwOFKu~#d)U2SQ5eAmekou^P+|X}tde9@Z%R@%GQYGyCZlx<24Hx6orF z*l0cYhmVG^--QQ7cK!ncy-aW@D3TP8*!KT@Wrz|TinYH@gRKlipx;-9+tEOXD+3Yi z_mxrlpTYilWo-O4CeoFGcvt~@9OA%Bt$&u(A0P)`@vwDw=zhP_yX67hhGd(B*gAD45@$&DbmkgHhgpu z#R;_EyMIATL<68z{&0CpbTrT*6&Pz?kvKnt2On`LRS?@0`*2XI)c*|}j4FsZ p8p;$zT@M4F2Uz~+<3uQFKbf+Rj1I^pX#1T9B?8!@g#Td2{{Ums3BmvX diff --git a/test/src/test/resources/hudson/maven/big-artifact.pom b/test/src/test/resources/hudson/maven/big-artifact.pom deleted file mode 100644 index b7812b68e8..0000000000 --- a/test/src/test/resources/hudson/maven/big-artifact.pom +++ /dev/null @@ -1,67 +0,0 @@ - - - - - 4.0.0 - test - test - 0.1-SNAPSHOT - - - - - org.jvnet.maven-antrun-extended-plugin - maven-antrun-extended-plugin - 1.39 - - - package - - - This is my artifact - So is this - - - - - - run - - - - - - - - - - m.g.o-public - http://maven.glassfish.org/content/groups/public/ - - - - \ No newline at end of file diff --git a/test/src/test/resources/hudson/maven/custom-plexus-component.pom b/test/src/test/resources/hudson/maven/custom-plexus-component.pom deleted file mode 100644 index 941a182de9..0000000000 --- a/test/src/test/resources/hudson/maven/custom-plexus-component.pom +++ /dev/null @@ -1,34 +0,0 @@ - - - - - 4.0.0 - test - test - 0.1-SNAPSHOT - bogus - \ No newline at end of file diff --git a/test/src/test/resources/hudson/maven/envars-maven-project.zip b/test/src/test/resources/hudson/maven/envars-maven-project.zip deleted file mode 100644 index 802d3c0e4a4b2546c69ab679c2934bdee196c304..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3374 zcmWIWW@Zs#-~hszt2OKxkN_tG3xls~h@-BjpPT-_Qw$8u3<2Kk93T};P-Ou)HH!dM zz%~0i`gyv!28ZbRx_$ONbK1vSSMMUPx31Q?Gv_x48C)@b@U%$J%U8$K_hRWP7S0(j zC67o;YGtrYf2R2(O-=l%c(~Z~CC|jPE1s#o&iqvLv4|1ukXcQ?4c&p(fH1-#$9qtH z!3C7f&q>U!)GJ9XF46YX)eAgv-t)}bCrn^NEDk+$;RPz;Mlpo77u67Uplm^Yu3klM z&e707|Jw!vbWo>|_4n_V@7q0XzRgtD%mWcCuYP=@(5222vdVbnr^hGXo)!O9S2XFvsTb}ZvW^=z z9iP0k`0A96K_&~70y|4MCz@IA-Zf|AVvTQ(Ohvy^p0e$YTD$1#?FQ?f$IRc7%k12f zG*^UNjI?0Bu^=frSs zTaDZl>Dch}$Vsn^w4!UmoVH5rew0)DB=L~NPlcfNw40X-Y>Fyf>bIUu{L*($-*5f1 zc>R2fY`2wDG!$o6u6$ChdwG7!I|DXL(}=cg?FTvvHeYy`9%(FXxs-5w%a{8VmmgaR`^`>thHJt8n9>4t_F_UIlYHU z3XL-sTo9e%Z!KISbIAX{;-7UH`(mzL?|!KjAbD;j%kIDnyKkK{eDyGSZOxUy`-eWR z2((SwUCOv6@5(>p6>7iAste1CzyGYNE35pt>FQC2ZJJ$vHJ)*+>fdkKm|Lj4`}%qD ztvbJ!?9qB9W1b|ub@TOSEA-yH-E}theD$*cEk)*Rn^lSWJR%A@7y7n_)}EIrz?F*pL25I?Zk_6DU2x#1ebT_UfIw(yY{m~ z%-p}vZ#8vJ+to5X@8_-^hp%nC?^>|hC3X6(*#GLKum8+F<-5Kxr=06>=iS=lCk`Jg zspmU+Pal*G|EX-0ZUN>)Up7eU2c>fc2K3a=0u(DQN(Sm+X2Gt46RHA~JMbvuLspiR zSeA%Ktq_LVSeI1&#eUNDoD($#G_MzfX>8%0T$v@#^zWV(@FIUC%4ZX8wD6ZNh z5^zY{xS@h??pJ||j~ySL%t_fGr1!3WSL3r4P9B$47?^f^n0#RiW19cRsmCKtLpi1U zy98eFy8l(%=$5l=&D=*hl6B8|5`s40edIUwzUBpyr4723xxZE)(%O7XXQflgi|+V} zxeL2@`0oDZadCEz?4`~kzBuj;$8R#-UTV-N@^NzhB9pfr6#nz4Dd``FW!(4s zDf+v%U%YK`PfAWc%v;fJO9QoiJ5tL;uV(Kz{-eBZ3wl*r8ZRcq@*@nI`OAA4QOh^ryH2sz$s0)cOzf30gp?0 z{n4o_4O;jUUR|5J(D~>Cww6U|N0b(duKMwP*M-SXC$BBHO8ot#u%Y*0OX&6wTcpG? zJ6^3)3faMT&wbD1H@_#}vobyT>BG50Reqe}f*0Bs{Q4jKY|Xc>b?yn~b1y!->HByu z^Z8__orm3@EHCq|;edyz#|eszB1rkgK&&3-X~BKLOIjAK1O_iq7W+*K{8 zX#grl^hzp9K5LxS2)%ImviA8C;U{#$G){WxdTo9F6jJG?vCh6K2~;c&Hja@=gaNe$ z0&8A?S|F$Z-lPDvM*_T2wIVkNKbF2jtcOq_~8caSX^nG$WAp zf!Yelg)ykDfB@XUECFpffHZM2U@wJH{dt5LR%+wc0V=l<;2Bg0v=qm!4V1GGfEQ>l zsCocvgXS~bdO_tn0$5_`h2%Tj`a$J90%T&p(4ygD<0DmG(gB7O8nH3cE$fXasHv>1;5XglUU;*B&K)*9E2m#>? KV3KG6D+K^07vr%2 diff --git a/test/src/test/resources/hudson/maven/foobar_eclipse_with_fix.zip b/test/src/test/resources/hudson/maven/foobar_eclipse_with_fix.zip deleted file mode 100644 index 5d7f2f2cd5d2ad00f32789209bd4e65b9eb96b1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10089 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

    Bg-p$Q=lbCEE=}K);nYs$`*XYZAEK0)jUS+&q zcrsUF!vhFlPJ~OQz7^x9u3nQQi8q-wH8aHTN3XJ;y~L-S=O$(%e*V*Rb9ylSc>TMY z#T2v0Hyo~5_D~HSRef^mI|z;4gx6ld<4+a;#%1Rapift)PW>)IXH zo#Y>cO_Wy@D*Cq&$%SE^b);qWULq8Ysx;)}?mkCxVR!vx#z-}0BaHcG`t4|TUmIg* zMd0`RUzQb}yk5YeJ^z6Ze-H>A3&M0sfa3SuzDJ;#cf_eI>Pv+ai&2Z3Y z5Xe!sPj50=_5f$s3j?1S0bD_WkI3I}hVu0rgz>j-)Z(H4)@0iy!sl?Nq9AwE$T^e_ z=vxT)e^BxNxXeXH)OYjov(^Afcd-VBG&p6j@{wG_^A|#okOvMPxiZpKUDov}%Q(V- zzWTLf`f5}h4Wr-?wyp*?4i)as^%nTf3N^@srWL?%+pcR&lfidbr~yvq2fl8b3|{Mh zQf&7iSbw2lVSzXxg}dVXdBX*w?f#An1;`q7|MIh`HlhwH~#1VCeUAHC3@dGHRI6{&hHI|w)>kmzEe9J4U2zp<C|xpUfq>Vz8L z#!uky4f@9_ZjW_)9~cD-K6>B3f!*DKZeMVF!xgXMBV1QYaG{aybX<0+8-czw_?f$&{CrY80FRdgYH9=!NwOF z`_*4#|A$`f)CfZ@045zL>Aoli8Tl3b#sVr^+hUl=0{c&>0g}Z6+CuI~$Pi+-aF>rRy$DopTr^!^R();ezUx!9@H oK`%l86a>3!>h_uwrQ92ZkFEwDU@Nh(IDnsYVl1rUjl$x806GgyGynhq diff --git a/test/src/test/resources/hudson/maven/incorrect-inheritence-testcase.zip b/test/src/test/resources/hudson/maven/incorrect-inheritence-testcase.zip deleted file mode 100755 index 0a30bcca61d4003e96117ceb532d736c6669c095..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1004 zcmWIWW@Zs#U|`^2Fx{eLw^5IG9XBHb!!;mgXOLki$j{ZQ$ju22;bdSoeXrn~wm{jp zw1S&~k>v$50|S`oop{#ouz^6!dDmCgAv=%1cJQCrd4zY#v@kBGJ6ex*Iu$qS-u_ux z^JdG^rwvDP%m2K6(|4xl>U@nqS+Am=zMFikdNOlKjf3lshl?B+$ znxaqba>pn3lpB|OXR_tr_LXF_p6B({CY)*V)3ZuGPx7XjM-}LA{dIiT)%YViOAh~X z;Gg2N&Z}!fQ|Ycl-$d7PzdM?9;K6jUg|}Yavk7(Gpy(TKxi(0#)BQPX`H2UA6pe%8I7!ot=uv>eesg7@&kZj$ps8c28L&wmF&O_C;u;j(^& zjOz2l)dnl>1a5n<^`diqyy(4|CeEo_PqnDd-LUZ9K)|StoQ?{(CJ1WJVpE2V{st8tZTYCXSu|Mg zialoW>GI3%fB*6Zcr!A|G2=?)65uET1~J2uMi3JovaFCKkCxmahT)23h+zy20t{~* weSl`d;~Z!hD9*7OhAYOAUDk^*3^o3N27}@s!(di6kY|{I@H3Ep%mm^A06#8oRR910 diff --git a/test/src/test/resources/hudson/maven/maven-compilation-failure.zip b/test/src/test/resources/hudson/maven/maven-compilation-failure.zip deleted file mode 100644 index 771d0e861a0ca63fd43788078fc997146ac6533a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2160 zcmWIWW@h1H00Dzi9&0cIO0Y19G87jj>xYJLGBER)H+${y?C=V$U}1Q{{DL_Er#4Qg zwvyE15`^BTMCs*2)|-`BmPo1r!WahNcZLYiuN(|vJv`PqyK9`q7#SFPm>3x3v76#p zP!Iz2lpe@o;6Pat-|Pj#ut2Guc+l^#0Z-feN-p``UR%n`ByY`S*5;qEHIMW4q!XSJ zLbrB5-l6N0e5T#))c@b##jn5qwt&5*Lvh_yBlq_jhpIQJh1tGTo+l%_%I%8vqcHY8 z**8>$Z!UbbDJ0;Kwsnfymp+Ap#~mM@)J{G6j5pnPw&9c2Q9rlH`*Ocs;e6uQ98Rts zAB-2hWzvy&<-IO7V4cvm7OSEsLbubdFVUUWd%Z{dlW}tMS^4Yt->iH(r!j2`Q`y8G z**f8{N3$Y|TAxhp;;z-b6d8Ltk0V)0d$IGqSp@~r4Yqp98+Nk_FY2o~k+)>)j%718 z>&|=de7(qN|)*XzjE&Pm#$ ztA3R`9oST|%lh`F@Lwz0m<Ak7J&)NJuOc@Goa(m& zy|NU^JH3+(^MT%JEw5{xYP&61LhFg!t7~2X6IQIsJ5kh^Fzd#&w<=%mo0m1HF$+b% z+k3wJyI%3yj1dM-1@S=hi5GhvOGVvA*C(gf6~iEdoDFque{3q_=2qxSLW^;mu-)xhpSZD zH(lM;@QQoQ>)p>cGAaw}IxM}~Td7~ff6lmQZPVek)7ka@oR#4``2P6+4?(Mp>3v6urM9MCevjR%r+jeoF?N3;mDD%C(Cnm0uKJe7(?{FhCrTT| zriUEwP5;ZhVfj`hNizVb3ypLfJBG5p^3L-t=DxQ!7O_dqRYhY)AO+yq` pK+j?`6|+P@HkE^g0Z~2xO#qb-5O=Y%0b_}Q3kaWs{J{$10RYR!f^Pr- diff --git a/test/src/test/resources/hudson/maven/maven-dep-test-A-changed.zip b/test/src/test/resources/hudson/maven/maven-dep-test-A-changed.zip deleted file mode 100644 index 853579d789d9b31e1a9b2cd35734668b37514a94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2597 zcmWIWW@h1H00F~N9&0cIO0Y19G87jj>xYJLGBER+H+$XL-s%-v!NTx@`2}+TPHmh} zZ6&G2B?!IGiPFo5tT!vMERm1_Y)O15ZWhKc0O5?^KqqkSK=pzM(61Z}AMN<8XQV|; z5@2Lts9|DYP{3}AV?jX(&{K|jAeVtd;$e`JviLrYoa5$O3#~Bb&sdpx0ZW?ESfR`Qt!0|v!6cBGtk}A%%`|_ z85^(0gai6+w^?WSTrICVmm54~gW44x=Dq#SjJ~)S33_aJS@1W1ckNN7<-G=)PG3BZ>etR(x_Q^}SivHd*?XO%g4iZ*Vz8d0vtjmP&NY$i zTui2keHWR2Pt3pIZ_6VqS5v(o@ms<-mrsn5+0J|3zuU6QKYbUEX6|fzmZXLw(Or*D z7u~ya?iD-XRFa#RnTLpE;!+qam4Nl4rjol@liAS(?5PA{03wwv0!HV3G*cj{1Y}BJ zOhj@gFwLw2Vi{;EK{ExIN{~_r$hg@^DMYt-BVV(Dfb;j?T+>$?9AGyv+-mL2enn9w zP{eeChGo>kuKN60*EW@G`m=`H$*$#M!rUF|uV%Gfc(7{j=Zc>%u4q1g(|5?sT3d0h z2d6U!^9$b-zr;IPpMU-mmMB9Q8G6$yq$-val4)j++ey(0cZVot+R{#?^2*a{| z@1%oyhZTT{ytYf@J40;s46U{+x7D_0FZH>cC#n?ZHO+xV<>kFPhg-UqA-8z{|I7Km zZNb`$q8!s>W<}jx{^d=~?t?oX+G-q$);rg*Mf2mw4ac)x1U%h~iZ_0mF4xA$yZVH! zz@{VezhmEVOV9I6l?`XQl%z64htcZZFcRTIDVlZFBaCUKpM5Sn&Aw z7i{O>`aF7ZLq6wEN6trZi3tgCP@&~>Vv8R?!Q#ddRNf$p8&C=M0V!=_6gP~8_Y*g; zbF94$EN;9Y#mx?s;)ZXRyF$;FkW(^yxmn~-O0C+ppueK%VZd9LT7|rlBki2GQ#y=8 zVyAI=RXpbT*m91wBgR5u&EEA9Yo_}DK4@}*$LiRhUFT#S7}@ItZDd%NxV@}n|Gmm9Bjc($#c;|2rsTc@P>N1vaSh1myqGct)YLYLaMgV3I+yz+Bq4JwVOk=LrV=J&EP{ZqZru?Xd#BfAk0b**)dx%3_>^ylwc5E zgQPyp$_`>0FflSLY5axVG_ zf2d&=`pBVn3&S8psDa9Cgx4UUhFSO{hnfO2lGnh_0-J_Z6##vU%~;H`7unb!Kx5%0 xG0+4^DGW=gxC#?w6EayL4g)&})nOrsq6KI)BBijh0h0g&HxRl2tv?Os0RUWvH!}bL diff --git a/test/src/test/resources/hudson/maven/maven-dep-test-A.zip b/test/src/test/resources/hudson/maven/maven-dep-test-A.zip deleted file mode 100755 index 719c6618efab37cdd92478bf3b8afdbe7d5d134c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2593 zcmWIWW@Zs#U|`^2*k;FPeJj;(k~t#-!(=7~26hHfhJyTDy^7qN&=5`r=8CFTFOKc4 zUZE8%3@?~pF!xS6n0Hu#r|o@hm&SL7*yrpL^Ry1D$zo0#1PcRaM!I1;UQu3?Mj$B!G1XS)b^x)&91 z{4`yzjgfct30r|pN92FUzT=jj=b0)S&UDFj(vPm*-cNqq@0Nu9mzaJ>dC`QguZ1lI zOE$Wm`onSS1=Fi?gKOpK8ySxp>pCpG82dZO;67XEUZ(bQv58xqc&Bq&Zs>a?EnJZ| z=g*TvKQ}a(aXV~VTA{>#D*E@Mr6H=TcP#v&&vWvhRb8jYwEcPgKR-Y0eN>m^^^q;@ z?P39=xhrqZ)eexE#Q16I0 z>=V5(I^nV4@$WC#&cF3}^x}qm&YzB)kIVtU0OtY*I0J)WDUUUn0VP;~0bX2`tPc)t zUh`(Jx7!e*jawThR9kLhW*$QCMWmR*t(Om3Z&qSiBEkSJtOnc!CK6;f3u72il3H8> zF=ZDp!JwN0NotRQrrh50`d};2s1-mggWVLzf&xc9kgLEUauUrr-QJCS%?1L_-+yyW zUukfF-C$#B`ilU=C5l~(*o0KwSFd_>znXWePUQ0HX^SuMr}ExNdDppXnWD)KiTBKL zia&pw|Cuh;c?Zu%Oo=`(VY&0_J+&1n#;=&(6#O_CnTu8Kh1V+n@w1`Opz!IPaSOO?OqXo?rV6=n)BN-`bo&uu=gh55a`fx+d z+KC7A4jBN8hDt7Z@1XM6Po&nI`sKWcocZ|AN0QkE~ox^?t-}3Ex~kF-B%P?|J`j z%P#-)T|An(v+Y@u8jeJFJvv=<@6Neb?4VQv3F7O(h*CYzWWf&%;%dA>><9{DMCxCL z7Hb$m%vg9oaRWQY+S|B-m~WSR0?(e1Q!;y%+xjzj!)0G8&y$f|6&ohsAuanM@sOF+ zG@j7uOPhr5sVG-FJ?Cf=;p51L1aX}KuL&rK*9Eh)3jH{mahqZC@?Yo9@67HJGHaQY zI`4a-T1L>d6~3C<>sxhC>mTAUlU+A^?aI%!jI#o`ZWu}4z8|Gibcb2R?KRc~hdi$P?^FhMptl@*uK$VCg#Xi(7taWyL&D0grJp$pKL$H6=RMe;CB diff --git a/test/src/test/resources/hudson/maven/maven-dep-test-B.zip b/test/src/test/resources/hudson/maven/maven-dep-test-B.zip deleted file mode 100755 index 21fb317024853d4761e7c458600dcc5128a18e05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2606 zcmWIWW@Zs#U|`^2aJJ{OHuQO@=*-B#V9m_Hz|J7bP>`RiSCN|&8p6rITvy%d1;U{f zEDSH0UocOd?3;hcfT!*KpRQHk)e;M41c-IcHREIxy1YfYD`@?a#hzD}=>Goh9kfmK z$o0IBuaAFw?Xoa+X|aIWEz#&yxktTQpRzn|GG8E|zw-verO>wN-Ij97O&)h9PM$k) zf647_D`Z*U^-WN$n}0rR-G&F9A;*eT735qduAVVN;rx~dVOgbzcf61hY*`|?{^YC$ zb3K!vxm|EtzM;1H)Q)#LCJTNzd=3bExvR!+W_I%@w?^05R;E!4o4i|fB)mWJKdy|6 z{CTjoG?6RyQR9<|kLJD6On*B$Jt$S@x#-dQh5ly0KRUZix_Y`~XI0dmpgUiDG~*69 z{#N+m6PtT;X7qv^>b6g`9G5O=VR_uT@5@!r|MzXK%(+!pwWn5Lz1~ZSJLRQU)8n)& zZ(f?W<3U=>yNe%Aw0TP}^iH^Y>h8~$gUuFq)>PkkcX-1=^8jGra{&XNfx)np#~RFl z5-h;LFD^>f2Zubbd9&Ak+YWfhnTODOkSM);$a=F9%MuA0P!uSD;$~qC z14>ehOCY8k2Rh*qnlm8DE(n-WOMg8Pg&m|L#BHCoUqlkPLC;=3hhYiH??2_8Rl|Ie3Q zK6~#wK?&RQQ;poyJ0B|Zw5{&@BIrNu+f}ob{70sK`(cnM%;Vj*>U5AJXa1y#HVZei z#BuR+=${K!x^}Ad?m?3aGF!fXG&PH8V&eRhBooEIc;zp@^DA?Egu+^8r7rkxBVuBx zot=E@P-dg|r_~OJ1o&_JUc2&gpFpn;YtD=_7w$)$35sg#eAw))Rrm6<%G|5A@0uns z^PaqYTvb&-^Yezo7nyErN}Zqew994ZmL1D?e3ARWa@}8JU2sU{!T*ca`sOp=oUonu zy#Hp+F8}OXESkB~>p6}vbZ!?pGX3eEopZ19gJK$z@}-jato7!*6A{EtpfE8O3H$<4+fQqFPO(V7bY%DWLQS7Fhud9F<^TTwrsD3K(tZbt%#~9t zE5teMCYgqmI?8`|acJMIt}6GK*B#};W+w&GSM1Ueo$}$BqOY=)SN`P%ZeY?)gh*?wqe1W>qZsUZ{~#bZv#NruKSCzbE2t zEVKEqpS^aaXnv>KG$u2Vp113D#U_~@deU(+S)=Zy_e8%}V&$R}f;K#o{XB`K<+Re9 zf=vg?`qU<=?Mtx?in@^eHC}KL3ryAwOBz8;cpb+IspCQ`7#JWX;3}FS zCNMAvFuZlF1sVh?sDau+xtfr6P6lM{TcFyZ1vZgp@S<0oe>_34y~P%px1vF^0f= z4)P1wF<^tBB?ZtlPyvqc8m^)nIc&?Zn}$@71C0e0|j!AV(scMog%q zhFCkY8TdmCvou8xu@e{uA)Ey&TM=G^gcxS2iX38mEJ$8Mgg;st3k)?#`3g&>xJnab hqYGJa8I4@F0F4HfEf80;vVn33HxRl2eR&1U0|4Y9Hjn@S diff --git a/test/src/test/resources/hudson/maven/maven-dep-test-C.zip b/test/src/test/resources/hudson/maven/maven-dep-test-C.zip deleted file mode 100755 index c789f277b15393bb48be64c62f3b98155a600858..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2610 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 bQ2eGMB{me}S=oTP8Q6gE4$uHoCJ+w*_on}1 diff --git a/test/src/test/resources/hudson/maven/maven-multimod-incr.zip b/test/src/test/resources/hudson/maven/maven-multimod-incr.zip deleted file mode 100644 index 71e0e08e3aac1132cc5c24b766efbabd7198f28a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10295 zcmb7~2{=^!7stmEV_&kyV6wdSEhSCLmMkfIw#r&0lD#YmDP##z_A*K-vSf?*4OvqP z$yO3VC6Urg<$tfK_ujcP6aS}2>3O=J`Tfp0-*cSr&DfBJmJ5YKp;2@3>v43o{nnXO zC={Lsg`!7sqr8rL$On2HvoL2s(V%lK7zkg!U|n+J_sWBkLDYBQkO7avF`l!>h{8U=~wkN}BY}J=yBwC|~u2_GxPaWFkgW_+!**&>a zVSxQZRLNfEFmo%cZEo{)fA?tWf$kW~*lRgc9ZhFZrce+&jFpa^ZeoU-Ts{ zVOj$#J9bXomK}`?x-q%Tq4t6&4KJQ~?)Ae?egFJe#xBh6Pft{GPltV;{jzQ6gMMbi zYjvCI>fGObV^(KIpQ#8LW$D^0iKhzAJy0AG6K8(aH!}-oTIr=(hvs)YX?Z8SB-7|T zKGyPdbX`M;hfz!au=2%5*CR%S&p(d5bqiSfmR089$hnJtnYq>Y&4y21QW+a>SLWeg zXfz*?*LTIBqzVpks-W>yOnoRi$3%^eip5n2+UZ=wIu6*~C_PQzu zA6)V_oY3;Vm}AEzEe^zId`~w0Aw2geXzay8>v6NW-~_ri2~vZ%6+&r@fvhqDSw*3g zW7p$WzWMbV8px{0ac93{E}9A}X+;4k{gDl&6?#<3kD2IEX4s=X-cE$aSyUs|_&5jR zabFi7-~X4vLymFO!QWvukSrn$0u({eu|B7fMus32kd`9-@+=1+m0)TVO7hP%O)oD^ zbKth};7M14jzouCtoLkCWR99S$tY}?e{bOg)rnz;yY%QggDxpz-kC~nZ(dH|Q?R`k zwMEC5rmjo%g5MXBAY}_k799@p<8E(8qo>_2jFT zE4lhbCK<9a#b@OnDKwzBgxIOtnoTTGt6b%}>!Mt->`~HM!6A{wu5NlFPod1$a-m8_ zE8f}1Uk}$VV#;HmU`AWvqjF~Rqf24UU2n4AbPc{?C|b^~+G`TG^n-W$`Mi8m>}bWv zzivvqXQ%dLV9f@1(sG79#Kj~HPGN_Ry}2EVqFjZ@GV*Y6^IR?a$Vx_*(F$WNWz@fe zl}HlEGJ-%*8P%LROQH}$8I1z8!SeU@X8~;%0AFH=GD6V6GJ;B|4S|)5D`AaLWY=C; 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 diff --git a/test/src/test/resources/hudson/maven/maven-multimod-rel-base.zip b/test/src/test/resources/hudson/maven/maven-multimod-rel-base.zip deleted file mode 100644 index 912f30cc8c2fb1d07486697fa7f6934942314b92..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8620 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 diff --git a/test/src/test/resources/hudson/maven/maven-multimod.zip b/test/src/test/resources/hudson/maven/maven-multimod.zip deleted file mode 100644 index ffecf2a09302297b9848ab049a96eb03ea880a67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8707 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 t0~&MUSHA!N diff --git a/test/src/test/resources/hudson/maven/maven-multimodule-unit-failure.zip b/test/src/test/resources/hudson/maven/maven-multimodule-unit-failure.zip deleted file mode 100755 index 9d1d6207d4c281f9b172363ec1f6fee976695590..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7771 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 AG5`Po diff --git a/test/src/test/resources/hudson/maven/maven-opts-echo.zip b/test/src/test/resources/hudson/maven/maven-opts-echo.zip deleted file mode 100644 index a0c358b94d577f1594658c3eed2ec87a7fd51b2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 623 zcmWIWW@Zs#U|`^2SZy%Ddg27fhSQ7;3

    k7}yzP7z*-p^(u06LPIzim=#}Yc&h<% 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 diff --git a/test/src/test/resources/hudson/maven/several-modules-in-directory.zip b/test/src/test/resources/hudson/maven/several-modules-in-directory.zip deleted file mode 100644 index 927ef95e7efc53b7b342f03942ababc5be65f6cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 870 zcmWIWW@Zs#U|`^2_|l_p*S{;VOOTO);S3OSF~~6F=BJeAr0P}V=7ffDGBBTURQIjP zR`)Hf;AUWCdBM!U047=|9`rw~Aad;eZ*7nFtjnxtv~H|0oRzcgjJgshF{a!q`6FTlG}}-nOPI}zLER1JLhEN<2Qj_ z4N5tGl&o7WoZ-B9!K-;8W8s7wyShYXo6bzMnxRs3ullQVd2IXc+ehvOSgGc}zG`Nc zbzSqxKZg`0nqGTeVU}?2;_k#JyG;@vG*z(+J~EA(lXddMR1<;ti=SrX?5o|Uzpa0^ ztn%BdD|WnR4>8QIV>y1}sg($C#!2piODs!xq}m@y=qax>>EwNU|HDc30C=D#l&jlq zJ?<7S3k=X7K+Fyd)Pnq6c!;XV;0RG)|HB46ZSQ}JhQ9N)y1^Klsiw!Vq&M`H!<}Us zys-;^229#|cfY!)yCnM&)xP^S+mfZ%yI)kbHQyGxiNSkDV~h8)S7BzX6*ujdlqdLZ zFniT(JiQ{~7L&}}%a%?}-(s9rX<8Sp@M%7ES8Q#9*d!Mt`CRMEhYb!qG%GTX+~$&h z+xICmt8a9s?9`s$8n>UBbx9vx(Ov&*d&DPk!PYJDvYVXQ+@qN{c?ju*oZWqWWwMZP z!WVDufaEKl_E%dp8IRtaKkL-y^J@)%)p1FfuB%;srt*40{?vmnytq%KZadO%wszk8 z)2|A*A78P5mFmg|OO~{Gw+EIqy}JElNpC#!MX!V}kK?K=O7FD&elzi;gZ_8&m=%(Q(ULE+d6@BqY+fkPSa|FK Y4Fkm - - - - 4.0.0 - test - test - 0.1-SNAPSHOT - - - - - maven-assembly-plugin - - - bin - - - - - package - - single - - - - - - - \ No newline at end of file diff --git a/test/src/test/resources/hudson/maven/test-pom-7162.xml b/test/src/test/resources/hudson/maven/test-pom-7162.xml deleted file mode 100644 index 2b6f190398..0000000000 --- a/test/src/test/resources/hudson/maven/test-pom-7162.xml +++ /dev/null @@ -1,26 +0,0 @@ - - 4.0.0 - - com.infradna.support - query - 1.0-SNAPSHOT - jar - - query - http://maven.apache.org - - - UTF-8 - bar - - - - - junit - junit - 3.8.1 - test - - - diff --git a/test/src/test/resources/hudson/maven/test-pom-8395.xml b/test/src/test/resources/hudson/maven/test-pom-8395.xml deleted file mode 100755 index 0ef0ff59ac..0000000000 --- a/test/src/test/resources/hudson/maven/test-pom-8395.xml +++ /dev/null @@ -1,25 +0,0 @@ - - 4.0.0 - org.hudson-ci.testcase - testcase-wrong-plugin-dep-scope - 1.0-SNAPSHOT - jar - - - - org.apache.maven.plugins - maven-antrun-plugin - 1.6 - - - ant - ant-nodeps - 1.8.2 - test - - - - - - From 3ba17df3b3a52f6dece82e5da3e95287fd4a64c0 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Fri, 4 Oct 2013 13:43:48 -0400 Subject: [PATCH 54/54] Moving one test case to maven-plugin. (cherry picked from commit 261f40533ebd5bfd34bc6232052e51c12c88d5c1) --- .../hudson/tasks/junit/CaseResultTest.java | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/test/src/test/java/hudson/tasks/junit/CaseResultTest.java b/test/src/test/java/hudson/tasks/junit/CaseResultTest.java index a83278ccd5..c9ae7ada21 100644 --- a/test/src/test/java/hudson/tasks/junit/CaseResultTest.java +++ b/test/src/test/java/hudson/tasks/junit/CaseResultTest.java @@ -28,16 +28,11 @@ import hudson.model.AbstractBuild; import hudson.model.BuildListener; import hudson.model.Result; import hudson.model.FreeStyleBuild; -import hudson.maven.MavenModuleSet; -import hudson.maven.MavenModuleSetBuild; -import hudson.maven.MavenBuild; -import hudson.maven.reporters.SurefireReport; import hudson.Launcher; import org.jvnet.hudson.test.HudsonTestCase; import org.jvnet.hudson.test.Email; import org.jvnet.hudson.test.Bug; import org.jvnet.hudson.test.TestBuilder; -import org.jvnet.hudson.test.ExtractResourceSCM; import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlElement; import com.gargoylesoftware.htmlunit.xml.XmlPage; @@ -124,27 +119,6 @@ public class CaseResultTest extends HudsonTestCase { errorStackTrace.getTextContent()); } - /** - * Verifies that the error message and stacktrace from a failed junit test actually render properly. - */ - @Bug(4257) - public void testMavenErrorMsgAndStacktraceRender() throws Exception { - configureDefaultMaven(); - MavenModuleSet m = createMavenProject("maven-render-test"); - m.setScm(new ExtractResourceSCM(m.getClass().getResource("maven-test-failure-findbugs.zip"))); - m.setGoals("clean test"); - - MavenModuleSetBuild b = assertBuildStatus(Result.UNSTABLE, m.scheduleBuild2(0).get()); - MavenBuild modBuild = (MavenBuild)b.getModuleLastBuilds().get(m.getModule("test:test")); - TestResult tr = modBuild.getAction(SurefireReport.class).getResult(); - assertEquals(1,tr.getFailedTests().size()); - CaseResult cr = tr.getFailedTests().get(0); - assertEquals("test.AppTest",cr.getClassName()); - assertEquals("testApp",cr.getName()); - assertNotNull("Error details should not be null", cr.getErrorDetails()); - assertNotNull("Error stacktrace should not be null", cr.getErrorStackTrace()); - } - /** * Verify fields show up at the correct visibility in the remote API */