diff --git a/changelog.html b/changelog.html index 8346798549..0d5ae6f271 100644 --- a/changelog.html +++ b/changelog.html @@ -59,6 +59,9 @@ Upcoming changes Flyweight tasks should execute on the master if there's no static executors available. (issue 7291) +
  • + Download tool installations directly from the slave when possible, since this is much faster than going through the master. + (issue 17330)
  • Improved UI for implicitly locked builds. (issue 10197) diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index 25362a7f94..3dabd48108 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -493,9 +493,10 @@ public final class FilePath implements Serializable { }); } - private void unzip(File dir, InputStream in) throws IOException { + private static void unzip(File dir, InputStream in) throws IOException { File tmpFile = File.createTempFile("tmpzip", null); // uses java.io.tmpdir try { + // XXX why does this not simply use ZipInputStream? IOUtils.copy(in, tmpFile); unzip(dir,tmpFile); } @@ -504,7 +505,7 @@ public final class FilePath implements Serializable { } } - private void unzip(File dir, File zipFile) throws IOException { + static 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") @@ -724,6 +725,19 @@ public final class FilePath implements Serializable { if(listener!=null) listener.getLogger().println(message); + if (isRemote()) { + // First try to download from the slave machine. + try { + act(new Unpack(archive)); + timestamp.touch(sourceTimestamp); + return true; + } catch (IOException x) { + if (listener != null) { + x.printStackTrace(listener.error("Failed to download " + archive + " from slave; will retry from master")); + } + } + } + // for HTTP downloads, enable automatic retry for added resilience InputStream in = archive.getProtocol().startsWith("http") ? ProxyConfiguration.getInputStream(archive) : con.getInputStream(); CountingInputStream cis = new CountingInputStream(in); @@ -743,6 +757,31 @@ public final class FilePath implements Serializable { } } + private static final class Unpack implements FileCallable { + private final URL archive; + Unpack(URL archive) { + this.archive = archive; + } + @Override public Void invoke(File dir, VirtualChannel channel) throws IOException, InterruptedException { + InputStream in = archive.openStream(); + try { + CountingInputStream cis = new CountingInputStream(in); + try { + if (archive.toExternalForm().endsWith(".zip")) { + unzip(dir, cis); + } else { + readFromTar("input stream", dir, GZIP.extract(cis)); + } + } catch (IOException x) { + throw new IOException2(String.format("Failed to unpack %s (%d bytes read)", archive, cis.getByteCount()), x); + } + } finally { + in.close(); + } + return null; + } + } + /** * Reads the URL on the current VM, and writes all the data to this {@link FilePath} * (this is different from resolving URL remotely.)