Skip to content
This repository was archived by the owner on Jan 17, 2024. It is now read-only.

Commit df517d5

Browse files
authored
Merge pull request #15 from raydouglass/pipeline-support
[REVIEW] Pipeline support
2 parents f07076b + 4c55d3a commit df517d5

28 files changed

+1194
-450
lines changed

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@
4141
<version>3.9.3</version>
4242
<optional>true</optional>
4343
</dependency>
44+
<dependency>
45+
<groupId>org.jenkins-ci.plugins</groupId>
46+
<artifactId>structs</artifactId>
47+
<version>1.20</version>
48+
</dependency>
4449
<dependency>
4550
<groupId>org.jenkins-ci.plugins</groupId>
4651
<artifactId>junit</artifactId>
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
/*
2+
* The MIT License
3+
*
4+
* Copyright (c) 2019, NVIDIA CORPORATION.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
25+
package com.gpuopenanalytics.jenkins.remotedocker;
26+
27+
import com.gpuopenanalytics.jenkins.remotedocker.job.DockerConfiguration;
28+
import hudson.EnvVars;
29+
import hudson.Launcher;
30+
import hudson.Proc;
31+
import hudson.util.ArgumentListBuilder;
32+
33+
import javax.annotation.Nonnull;
34+
import java.io.ByteArrayOutputStream;
35+
import java.io.IOException;
36+
import java.nio.charset.StandardCharsets;
37+
import java.util.List;
38+
import java.util.Optional;
39+
40+
/**
41+
* Root class for {@link Launcher}s that delegate commands into a docker
42+
* container
43+
*/
44+
public abstract class AbstractDockerLauncher extends Launcher.DecoratedLauncher {
45+
46+
private DockerState dockerState;
47+
private DockerVersion version;
48+
49+
protected AbstractDockerLauncher(@Nonnull Launcher launcher) {
50+
super(launcher);
51+
try {
52+
this.version = parseVersion();
53+
if (isDebug()) {
54+
getListener().getLogger().println(this.version);
55+
}
56+
} catch (IOException | InterruptedException e) {
57+
throw new RuntimeException(e);
58+
}
59+
}
60+
61+
protected AbstractDockerLauncher(@Nonnull Launcher launcher,
62+
@Nonnull DockerState dockerState) {
63+
this(launcher);
64+
configure(dockerState);
65+
}
66+
67+
@Override
68+
public Proc launch(ProcStarter starter) throws IOException {
69+
return dockerExec(starter, true);
70+
}
71+
72+
/**
73+
* Invoke <code>docker exec</code> on the already created container.
74+
*
75+
* @param args
76+
* @param addRunArgs
77+
* @return
78+
* @throws IOException
79+
*/
80+
public Proc dockerExec(ArgumentListBuilder args,
81+
boolean addRunArgs) throws IOException {
82+
Launcher.ProcStarter starter = this.new ProcStarter();
83+
starter.stderr(listener.getLogger());
84+
starter.cmds(args);
85+
return dockerExec(starter, addRunArgs);
86+
}
87+
88+
/**
89+
* Invoke <code>docker exec</code> on the already created container.
90+
*
91+
* @param starter
92+
* @param addRunArgs
93+
* @return
94+
* @throws IOException
95+
*/
96+
public abstract Proc dockerExec(Launcher.ProcStarter starter,
97+
boolean addRunArgs) throws IOException;
98+
99+
/**
100+
* Invoke <code>docker exec</code> on the already created container.
101+
*
102+
* @param starter
103+
* @param addRunArgs
104+
* @param dockerConfiguration
105+
* @return
106+
*/
107+
protected Proc dockerExec(Launcher.ProcStarter starter,
108+
boolean addRunArgs,
109+
String workspaceOverride,
110+
DockerConfiguration dockerConfiguration) throws IOException {
111+
if (dockerState == null || dockerState.getMainContainerId() == null) {
112+
throw new IllegalStateException("Container is not started.");
113+
}
114+
ArgumentListBuilder args = new ArgumentListBuilder()
115+
.add("exec");
116+
if (starter.pwd() != null) {
117+
String path = Optional.ofNullable(workspaceOverride)
118+
.orElse(starter.pwd().getRemote());
119+
args.add("--workdir", path);
120+
}
121+
if (addRunArgs) {
122+
dockerConfiguration.addRunArgs(this, args);
123+
}
124+
125+
args.add(dockerState.getMainContainerId());
126+
127+
args.add("env").add(starter.envs());
128+
if (workspaceOverride != null) {
129+
//Override $WORKSPACE inside the container
130+
args.add("WORKSPACE=" + workspaceOverride);
131+
}
132+
133+
List<String> originalCmds = starter.cmds();
134+
boolean[] originalMask = starter.masks();
135+
for (int i = 0; i < originalCmds.size(); i++) {
136+
boolean masked = originalMask == null ? false : i < originalMask.length ? originalMask[i] : false;
137+
args.add(originalCmds.get(i), masked);
138+
}
139+
Launcher.ProcStarter procStarter = executeCommand(args);
140+
141+
if (starter.stdout() != null) {
142+
procStarter.stdout(starter.stdout());
143+
} else {
144+
procStarter.stdout(listener.getLogger());
145+
}
146+
if (starter.stderr() != null) {
147+
procStarter.stderr(starter.stderr());
148+
} else {
149+
procStarter.stderr(listener.getLogger());
150+
}
151+
152+
return procStarter.start();
153+
}
154+
155+
/**
156+
* Execute a <code>docker</code> command such as build or pull.
157+
* <p>For exec, use {@link #dockerExec(ArgumentListBuilder, boolean)} or
158+
* {@link #dockerExec(ProcStarter, boolean)}
159+
*
160+
* @param args
161+
* @return
162+
*/
163+
public Launcher.ProcStarter executeCommand(ArgumentListBuilder args) {
164+
if (args.toList().isEmpty()) {
165+
throw new IllegalArgumentException("No args given");
166+
}
167+
if (!"docker".equals(args.toList().get(0))) {
168+
args.prepend("docker");
169+
}
170+
return getInner().launch()
171+
//TODO I think we should pass something here
172+
//.envs()
173+
.cmds(args)
174+
.quiet(!isDebug());
175+
}
176+
177+
public DockerVersion getVersion() {
178+
return version;
179+
}
180+
181+
private DockerVersion parseVersion() throws IOException, InterruptedException {
182+
ArgumentListBuilder args = new ArgumentListBuilder("docker",
183+
"--version");
184+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
185+
int status = executeCommand(args)
186+
.stdout(baos)
187+
.stderr(getListener().getLogger())
188+
.join();
189+
190+
if (status != 0) {
191+
throw new IOException("Could not get docker version");
192+
}
193+
String versionString = baos.toString(StandardCharsets.UTF_8.name())
194+
.trim();
195+
try {
196+
return DockerVersion.fromVersionString(versionString);
197+
} catch (DockerVersion.VersionParseException e) {
198+
getListener().getLogger().println(
199+
"WARN - Could not parse docker version");
200+
e.printStackTrace(getListener().getLogger());
201+
return DockerVersion.DEFAULT;
202+
}
203+
}
204+
205+
/**
206+
* Get the environment associated with this Launcher
207+
*
208+
* @return
209+
*/
210+
public abstract EnvVars getEnvironment();
211+
212+
/**
213+
* Whether the launcher should print debug information
214+
*
215+
* @return
216+
*/
217+
public abstract boolean isDebug();
218+
219+
/**
220+
* Make this Launcher aware of a set up {@link DockerState}
221+
*
222+
* @param dockerState
223+
*/
224+
void configure(DockerState dockerState) {
225+
this.dockerState = dockerState;
226+
}
227+
228+
/**
229+
* Get the, possibly null, {@link DockerState} of this Launcher
230+
*
231+
* @return
232+
*/
233+
protected DockerState getDockerState() {
234+
return dockerState;
235+
}
236+
}

0 commit comments

Comments
 (0)