New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[JENKINS-39748] provide Image.exec() to avoid disabling ENTRYPOINT in the image #78
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,7 +53,7 @@ | |
|
||
/** | ||
* Simple docker client for Pipeline. | ||
* | ||
* | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please avoid making extraneous diff hunks, particular whitespace changes. You should be able to configure popular editors to not touch lines of code unless and until you edit them. |
||
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a> | ||
*/ | ||
public class DockerClient { | ||
|
@@ -62,10 +62,10 @@ public class DockerClient { | |
|
||
// e.g. 2015-04-09T13:40:21.981801679Z | ||
public static final String DOCKER_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; | ||
|
||
/** | ||
* Known cgroup formats | ||
* | ||
* | ||
* 4:cpuset:/system.slice/docker-3dd988081e7149463c043b5d9c57d7309e079c5e9290f91feba1cc45a04d6a5b.scope | ||
* 2:cpu:/docker/3dd988081e7149463c043b5d9c57d7309e079c5e9290f91feba1cc45a04d6a5b | ||
*/ | ||
|
@@ -102,7 +102,7 @@ public String run(@Nonnull EnvVars launchEnv, @Nonnull String image, @CheckForNu | |
if (args != null) { | ||
argb.addTokenized(args); | ||
} | ||
|
||
if (workdir != null) { | ||
argb.add("-w", workdir); | ||
} | ||
|
@@ -116,7 +116,11 @@ public String run(@Nonnull EnvVars launchEnv, @Nonnull String image, @CheckForNu | |
argb.add("-e"); | ||
argb.addMasked(variable.getKey()+"="+variable.getValue()); | ||
} | ||
argb.add("--entrypoint").add(entrypoint).add(image); | ||
if (!entrypoint.equals("--")) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Better to make |
||
argb.add("--entrypoint", entrypoint); | ||
} | ||
|
||
argb.add(image); | ||
|
||
LaunchResult result = launch(launchEnv, false, null, argb); | ||
if (result.getStatus() == 0) { | ||
|
@@ -128,10 +132,10 @@ public String run(@Nonnull EnvVars launchEnv, @Nonnull String image, @CheckForNu | |
|
||
/** | ||
* Stop a container. | ||
* | ||
* <p> | ||
* | ||
* <p> | ||
* Also removes ({@link #rm(EnvVars, String)}) the container. | ||
* | ||
* | ||
* @param launchEnv Docker client launch environment. | ||
* @param containerId The container ID. | ||
*/ | ||
|
@@ -145,7 +149,7 @@ public void stop(@Nonnull EnvVars launchEnv, @Nonnull String containerId) throws | |
|
||
/** | ||
* Remove a container. | ||
* | ||
* | ||
* @param launchEnv Docker client launch environment. | ||
* @param containerId The container ID. | ||
*/ | ||
|
@@ -172,7 +176,7 @@ public void rm(@Nonnull EnvVars launchEnv, @Nonnull String containerId) throws I | |
return null; | ||
} | ||
} | ||
|
||
/** | ||
* Inspect a docker image/container. | ||
* @param launchEnv Docker client launch environment. | ||
|
@@ -183,15 +187,15 @@ public void rm(@Nonnull EnvVars launchEnv, @Nonnull String containerId) throws I | |
* @throws InterruptedException Interrupted | ||
* @since 1.1 | ||
*/ | ||
public @Nonnull String inspectRequiredField(@Nonnull EnvVars launchEnv, @Nonnull String objectId, | ||
public @Nonnull String inspectRequiredField(@Nonnull EnvVars launchEnv, @Nonnull String objectId, | ||
@Nonnull String fieldPath) throws IOException, InterruptedException { | ||
final String fieldValue = inspect(launchEnv, objectId, fieldPath); | ||
if (fieldValue == null) { | ||
throw new IOException("Cannot retrieve " + fieldPath + " from 'docker inspect" + objectId + "'"); | ||
} | ||
return fieldValue; | ||
} | ||
|
||
private @CheckForNull Date getCreatedDate(@Nonnull EnvVars launchEnv, @Nonnull String objectId) throws IOException, InterruptedException { | ||
String createdString = inspect(launchEnv, objectId, "json .Created"); | ||
if (createdString == null) { | ||
|
@@ -220,7 +224,7 @@ public void rm(@Nonnull EnvVars launchEnv, @Nonnull String containerId) throws I | |
return null; | ||
} | ||
} | ||
|
||
private static final Pattern pattern = Pattern.compile("^(\\D+)(\\d+)\\.(\\d+)\\.(\\d+)(.*)"); | ||
/** | ||
* Parse a Docker version string (e.g. "Docker version 1.5.0, build a8a31ef"). | ||
|
@@ -237,7 +241,7 @@ protected static VersionNumber parseVersionNumber(@Nonnull String versionString) | |
return new VersionNumber(String.format("%s.%s.%s", major, minor, maint)); | ||
} else { | ||
return null; | ||
} | ||
} | ||
} | ||
|
||
private LaunchResult launch(@Nonnull EnvVars launchEnv, boolean quiet, @Nonnull String... args) throws IOException, InterruptedException { | ||
|
@@ -317,7 +321,7 @@ public ContainerRecord getContainerRecord(@Nonnull EnvVars launchEnv, String con | |
|
||
// TODO get tags and add for ContainerRecord | ||
return new ContainerRecord(host, containerId, image, containerName, | ||
(created != null ? created.getTime() : 0L), | ||
(created != null ? created.getTime() : 0L), | ||
Collections.<String,String>emptyMap()); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -125,6 +125,17 @@ class Docker implements Serializable { | |
} | ||
} | ||
|
||
public <V> V exec(String args = '', Closure<V> body) { | ||
docker.node { | ||
if (docker.script.sh(script: "docker inspect -f . ${id}", returnStatus: true) != 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Going to clash with #84… |
||
pull() | ||
} | ||
docker.script.withDockerContainer(image: id, args: args, toolName: docker.script.env.DOCKER_TOOL_NAME, /* don't override entrypoint */ entrypoint: "--") { | ||
body() | ||
} | ||
} | ||
} | ||
|
||
public void pull() { | ||
docker.node { | ||
docker.script.sh "docker pull ${imageName()}" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -423,4 +423,54 @@ private static void grep(File dir, String text, String prefix, Set<String> match | |
} | ||
}); | ||
} | ||
|
||
@Issue("JENKINS-37987") | ||
@Test public void image_exec() { | ||
story.addStep(new Statement() { | ||
@Override public void evaluate() throws Throwable { | ||
assumeDocker(); | ||
WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "build"); | ||
p.setDefinition(new CpsFlowDefinition( | ||
"node {\n" + | ||
" writeFile file: 'Dockerfile', text: '# This is a test.\\n\\nFROM alpine\\nRUN chmod u+s /bin/busybox \\nENTRYPOINT [\\\"/bin/ping\\\" ] \\nCMD [\\\"localhost\\\"] \\n'\n" + | ||
" def built = docker.build 'alpine-entrypoint-test'\n" + | ||
" echo \"built ${built.id}\"\n" + | ||
"}", true)); | ||
WorkflowRun b = story.j.assertBuildStatusSuccess(p.scheduleBuild2(0)); | ||
} | ||
}); | ||
story.addStep(new Statement() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gratuitous Jenkins restarts. |
||
@Override public void evaluate() throws Throwable { | ||
assumeDocker(); | ||
WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "prj"); | ||
p.setDefinition(new CpsFlowDefinition( | ||
"def r = docker.image('alpine-entrypoint-test').exec {\n" + | ||
" semaphore 'wait'\n" + | ||
" sh 'cat /etc/hosts'\n" + | ||
" 42\n" + | ||
"}; echo \"the answer is ${r}\"", true)); | ||
WorkflowRun b = p.scheduleBuild2(0).waitForStart(); | ||
SemaphoreStep.waitForStart("wait/1", b); | ||
} | ||
}); | ||
story.addStep(new Statement() { | ||
@Override public void evaluate() throws Throwable { | ||
SemaphoreStep.success("wait/1", null); | ||
WorkflowJob p = story.j.jenkins.getItemByFullName("prj", WorkflowJob.class); | ||
WorkflowRun b = p.getLastBuild(); | ||
story.j.assertLogContains("ip6-localhost", story.j.assertBuildStatusSuccess(story.j.waitForCompletion(b))); | ||
story.j.assertLogContains("the answer is 42", b); | ||
DockerClient client = new DockerClient(new Launcher.LocalLauncher(StreamTaskListener.NULL), null, null); | ||
String aetIID = client.inspect(new EnvVars(), "alpine-entrypoint-test", ".Id"); | ||
Fingerprint f = DockerFingerprints.of(aetIID); | ||
assertNotNull(f); | ||
DockerRunFingerprintFacet facet = f.getFacet(DockerRunFingerprintFacet.class); | ||
assertNotNull(facet); | ||
assertEquals(1, facet.records.size()); | ||
assertNotNull(facet.records.get(0).getContainerName()); | ||
assertEquals(Fingerprint.RangeSet.fromString("1", false), facet.getRangeSet(p)); | ||
assertEquals(Collections.singleton("alpine-entrypoint-test"), DockerImageExtractor.getDockerImagesUsedByJobFromAll(p)); | ||
} | ||
}); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the developer guide: better to initialize the field with the
"cat"
value, have the setter callUtil.fixEmpty
, and haveImage.exec
specifyentrypoint: ''
. Also be sure to adjustWithContainerStep/config.jelly
to specifyand add
help-entrypoint.html
.But anyway better would probably be to change this to a
boolean
field, sincecat
with something likesleep infinity
(except with a more POSIX-compliant argument)The special techniques for step default values are the same for
boolean
parameters, but if you pick the semantics so that the default is in factfalse
, it is somewhat easier to write:rather than
and a simpler form in the Java class.