Skip to content

Commit

Permalink
Merge pull request #64 from jglick/installer-JENKINS-48674
Browse files Browse the repository at this point in the history
[JENKINS-48674] Fix installer
  • Loading branch information
jglick committed Jan 4, 2018
2 parents b34c6f3 + ca19d3c commit c7dfbca
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 31 deletions.
Expand Up @@ -34,10 +34,14 @@
import hudson.tools.ToolInstallation;
import hudson.tools.ToolInstaller;
import hudson.tools.ToolInstallerDescriptor;
import hudson.util.VersionNumber;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import jenkins.security.MasterToSlaveCallable;
import org.kohsuke.stapler.DataBoundConstructor;
Expand Down Expand Up @@ -68,7 +72,7 @@ public FilePath performInstallation(ToolInstallation toolInstallation, @Nonnull
}
String os = nodeChannel.call(new FindArch());

final URL url = new URL("https://get.docker.com/builds/"+os+"/docker-"+version);
final URL url = getDockerImageUrl(os, version);
FilePath install = preferredLocation(tool, node);

// (simplified) copy/paste from FilePath as hudson.FilePath.installIfNecessaryFrom do assume the URL points to be a zip/tar archive
Expand Down Expand Up @@ -142,12 +146,31 @@ public FilePath performInstallation(ToolInstallation toolInstallation, @Nonnull
return install;
}

static URL getDockerImageUrl(String os, String version) throws MalformedURLException {
final int i = os.indexOf("/");
if (parseVersion(version).isNewerThan(parseVersion("17.05.0-ce"))) {
return new URL("https://download.docker.com/" + os.substring(0, i) + "/static/edge/"+ os.substring(i + 1) + "/docker-" + version);
}

return new URL("https://get.docker.com/builds/" + FindArch.asGetDockerArchName(os) + os.substring(i +1) + "/docker-" + version);
}

private static VersionNumber parseVersion(String version) {
// any version that sorts before 17.05.0-ce
if (version.equals("latest")) return new VersionNumber("0");

final Matcher matcher = Pattern.compile("(\\d+\\.\\d+\\.\\d+).*").matcher(version);
if (matcher.matches()) return new VersionNumber(matcher.group(1));

throw new IllegalArgumentException("Failed to parse version " + version);
}

@Extension
public static class DescriptorImpl extends ToolInstallerDescriptor<DockerToolInstaller> {

@Override
public String getDisplayName() {
return "Install latest from docker.io";
return "Download from docker.com";
}

@Override
Expand All @@ -158,16 +181,22 @@ public boolean isApplicable(Class<? extends ToolInstallation> toolType) {

private static class FindArch extends MasterToSlaveCallable<String,IOException> {

private static String asGetDockerArchName(String os) {
if (os.startsWith("linux")) return "Linux/";
if (os.startsWith("win")) return "Windows/";
if (os.startsWith("mac")) return "Darwin/";
throw new IllegalArgumentException("Failed to recognize OS architecture " + os );
}

@Override
public String call() throws IOException {
String os = System.getProperty("os.name").toLowerCase();
String arch = System.getProperty("os.arch").contains("64") ? "x86_64" : "i386";
if (os.contains("linux")) return "Linux/" + arch;
if (os.contains("windows")) return "Windows/" + arch;
if (os.contains("mac")) return "Darwin/" + arch;
if (os.contains("linux")) return "linux/" + arch;
if (os.contains("windows")) return "win/" + arch;
if (os.contains("mac")) return "mac/" + arch;
throw new IOException("Failed to determine OS architecture " + os + ":" + arch);
}

}

}
@@ -0,0 +1,9 @@
<div>
A version of Docker to install. Tested values currently include:
<ul>
<li><code>17.09.1-ce</code></li>
<li><code>1.12.6</code></li>
<li><code>1.10.0</code></li>
<li><code>latest</code> (may not actually be the latest version even of Docker CE Stable! prefer an explicit version)</li>
</ul>
</div>
Expand Up @@ -25,14 +25,18 @@

import hudson.FilePath;
import hudson.Functions;
import hudson.console.PlainTextConsoleOutputStream;
import hudson.model.TaskListener;
import hudson.slaves.DumbSlave;
import hudson.tools.InstallSourceProperty;
import hudson.util.StreamTaskListener;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.io.output.TeeOutputStream;
import static org.hamcrest.Matchers.*;
import org.junit.Test;
Expand All @@ -41,50 +45,82 @@
import org.junit.Rule;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.WithoutJenkins;

public class DockerToolInstallerTest {

@Rule
public JenkinsRule r = new JenkinsRule();

@Issue({"JENKINS-36082", "JENKINS-32790"})
@Issue({"JENKINS-48674"})
@WithoutJenkins
@Test
public void testImageUrl() throws MalformedURLException {
assertEquals(new URL("https://get.docker.com/builds/Linux/x86_64/docker-1.10.0"), DockerToolInstaller.getDockerImageUrl("linux/x86_64", "1.10.0"));
assertEquals(new URL("https://get.docker.com/builds/Windows/x86_64/docker-1.10.0"), DockerToolInstaller.getDockerImageUrl("win/x86_64","1.10.0"));
assertEquals(new URL("https://get.docker.com/builds/Darwin/x86_64/docker-1.10.0"), DockerToolInstaller.getDockerImageUrl("mac/x86_64","1.10.0"));
assertEquals(new URL("https://get.docker.com/builds/Linux/x86_64/docker-17.05.0-ce"), DockerToolInstaller.getDockerImageUrl("linux/x86_64", "17.05.0-ce"));
assertEquals(new URL("https://get.docker.com/builds/Windows/x86_64/docker-17.05.0-ce"), DockerToolInstaller.getDockerImageUrl("win/x86_64","17.05.0-ce"));
assertEquals(new URL("https://get.docker.com/builds/Darwin/x86_64/docker-17.05.0-ce"), DockerToolInstaller.getDockerImageUrl("mac/x86_64","17.05.0-ce"));
assertEquals(new URL("https://get.docker.com/builds/Linux/x86_64/docker-latest"), DockerToolInstaller.getDockerImageUrl("linux/x86_64", "latest"));
assertEquals(new URL("https://get.docker.com/builds/Windows/x86_64/docker-latest"), DockerToolInstaller.getDockerImageUrl("win/x86_64","latest"));
assertEquals(new URL("https://get.docker.com/builds/Darwin/x86_64/docker-latest"), DockerToolInstaller.getDockerImageUrl("mac/x86_64","latest"));
assertEquals(new URL("https://download.docker.com/linux/static/edge/x86_64/docker-17.09.0-ce"), DockerToolInstaller.getDockerImageUrl("linux/x86_64", "17.09.0-ce"));
assertEquals(new URL("https://download.docker.com/win/static/edge/x86_64/docker-17.09.0-ce"), DockerToolInstaller.getDockerImageUrl("win/x86_64","17.09.0-ce"));
assertEquals(new URL("https://download.docker.com/mac/static/edge/x86_64/docker-17.09.0-ce"), DockerToolInstaller.getDockerImageUrl("mac/x86_64","17.09.0-ce"));
}

@Issue({"JENKINS-36082", "JENKINS-32790", "JENKINS-48674"})
@Test
public void smokes() throws Exception {
Assume.assumeFalse(Functions.isWindows());
try {
new URL("https://get.docker.com/").openStream().close();
new URL("https://download.docker.com/").openStream().close();
} catch (IOException x) {
Assume.assumeNoException("Cannot contact get.docker.com, perhaps test machine is not online", x);
Assume.assumeNoException("Cannot contact download sites, perhaps test machine is not online", x);
}
r.jenkins.getDescriptorByType(DockerTool.DescriptorImpl.class).setInstallations(
new DockerTool("latest", "", Collections.singletonList(new InstallSourceProperty(Collections.singletonList(new DockerToolInstaller("", "latest"))))),
new DockerTool("1.10.0", "", Collections.singletonList(new InstallSourceProperty(Collections.singletonList(new DockerToolInstaller("", "1.10.0"))))));
String[] testedVersions = {"17.09.1-ce", "1.12.6", "1.10.0", "latest"};
DockerTool[] installations = new DockerTool[testedVersions.length];
for (int i = 0; i < testedVersions.length; i++) {
String v = testedVersions[i];
installations[i] = new DockerTool(v, "", Collections.singletonList(new InstallSourceProperty(Collections.singletonList(new DockerToolInstaller("", v)))));
}
r.jenkins.getDescriptorByType(DockerTool.DescriptorImpl.class).setInstallations(installations);
DumbSlave slave = r.createOnlineSlave();
FilePath toolDir = slave.getRootPath().child("tools/org.jenkinsci.plugins.docker.commons.tools.DockerTool");
FilePath exe10 = toolDir.child("1.10.0/bin/docker");

List<FilePath> files = new ArrayList<>();
for (String v : testedVersions) {
FilePath exe = downloadDocker(slave, toolDir, v);
files.add(exe);
files.add(toolDir.child(v + "/.timestamp"));
}
assertThat("we do not have any extra files in here", toolDir.list("**"), arrayContainingInAnyOrder(files.toArray(new FilePath[files.size()])));
}

private FilePath downloadDocker(DumbSlave slave, FilePath toolDir, String version) throws IOException, InterruptedException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
TaskListener l = new StreamTaskListener(new TeeOutputStream(baos, System.err));
// Download 1.10.0 for first time:
assertEquals(exe10.getRemote(), DockerTool.getExecutable("1.10.0", slave, l, null));
assertTrue(exe10.exists());
assertThat(baos.toString(), containsString(Messages.DockerToolInstaller_downloading_docker_client_("1.10.0")));
TeeOutputStream tee = new TeeOutputStream(baos, new PlainTextConsoleOutputStream(System.err));
TaskListener l = new StreamTaskListener(tee);

FilePath exe = toolDir.child(version+"/bin/docker");
// Download for first time:
assertEquals(exe.getRemote(), DockerTool.getExecutable(version, slave, l, null));
assertTrue(exe.exists());
assertThat(baos.toString(), containsString(Messages.DockerToolInstaller_downloading_docker_client_(version)));
// Next time we do not need to download:
baos.reset();
assertEquals(exe10.getRemote(), DockerTool.getExecutable("1.10.0", slave, l, null));
assertTrue(exe10.exists());
assertThat(baos.toString(), not(containsString(Messages.DockerToolInstaller_downloading_docker_client_("1.10.0"))));
// Download latest for the first time:
assertEquals(exe.getRemote(), DockerTool.getExecutable(version, slave, l, null));
assertTrue(exe.exists());
assertThat(baos.toString(), not(containsString(Messages.DockerToolInstaller_downloading_docker_client_(version))));
// Version check:
baos.reset();
FilePath exeLatest = toolDir.child("latest/bin/docker");
assertEquals(exeLatest.getRemote(), DockerTool.getExecutable("latest", slave, l, null));
assertTrue(exeLatest.exists());
assertThat(baos.toString(), containsString(Messages.DockerToolInstaller_downloading_docker_client_("latest")));
// Next time we do not need to download:
baos.reset();
assertEquals(exeLatest.getRemote(), DockerTool.getExecutable("latest", slave, l, null));
assertTrue(exeLatest.exists());
assertThat(baos.toString(), not(containsString(Messages.DockerToolInstaller_downloading_docker_client_("latest"))));
assertThat("we do not have any extra files in here", toolDir.list("**"), arrayContainingInAnyOrder(exe10, toolDir.child("1.10.0/.timestamp"), exeLatest, toolDir.child("latest/.timestamp")));
assertEquals(0, slave.createLauncher(l).launch().cmds(exe.getRemote(), "version", "--format", "{{.Client.Version}}").quiet(true).stdout(tee).stderr(System.err).join());
if (!version.equals("latest")) {
assertEquals(version, baos.toString().trim());
}
return exe;
}

}

0 comments on commit c7dfbca

Please sign in to comment.