Skip to content

Commit c0a6920

Browse files
author
Sonia Zaldana Calles
committed
8273216: JCMD does not work across container boundaries with Podman
Backport-of: 9180d9a2f990e71ca6ac9c14e55a21f7372929ac
1 parent a128c80 commit c0a6920

File tree

1 file changed

+101
-14
lines changed

1 file changed

+101
-14
lines changed

test/hotspot/jtreg/containers/docker/TestJcmd.java

Lines changed: 101 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,22 @@ public class TestJcmd {
5555
private static final String IMAGE_NAME = Common.imageName("jcmd");
5656
private static final int TIME_TO_RUN_CONTAINER_PROCESS = (int) (10 * Utils.TIMEOUT_FACTOR); // seconds
5757
private static final String CONTAINER_NAME = "test-container";
58+
private static final boolean IS_PODMAN = Container.ENGINE_COMMAND.contains("podman");
59+
private static final String ROOT_UID = "0";
5860

5961

6062
public static void main(String[] args) throws Exception {
6163
DockerTestUtils.canTestDocker();
6264

63-
// See JDK-8273216 for details
64-
if (Container.ENGINE_COMMAND.equals("podman")) {
65-
throw new SkippedException("JCMD does not work across container boundaries when using Podman");
65+
// podman versions below 3.3.1 hava a bug where cross-container testing with correct
66+
// permissions fails. See JDK-8273216
67+
if (IS_PODMAN && PodmanVersion.VERSION_3_3_1.compareTo(getPodmanVersion()) > 0) {
68+
throw new SkippedException("Podman version too old for this test. Expected >= 3.3.1");
6669
}
6770

6871
// Need to create a custom dockerfile where user name and id, as well as group name and id
6972
// of the JVM running in container must match the ones from the inspecting JCMD process.
70-
String uid = getId("-u");
71-
String gid = getId("-g");
72-
String userName = getId("-un");
73-
String groupName = getId("-gn");
74-
String content = generateCustomDockerfile(uid, gid, userName, groupName);
73+
String content = generateCustomDockerfile();
7574
DockerTestUtils.buildJdkContainerImage(IMAGE_NAME, content);
7675

7776
try {
@@ -135,17 +134,26 @@ private static void testVmInfo(long pid) throws Exception {
135134

136135
// Need to make sure that user name+id and group name+id are created for the image, and
137136
// match the host system. This is necessary to allow proper permission/access for JCMD.
138-
private static String generateCustomDockerfile(String uid, String gid,
139-
String userName, String groupName) throws Exception {
137+
// For podman --userns=keep-id is sufficient.
138+
private static String generateCustomDockerfile() throws Exception {
140139
StringBuilder sb = new StringBuilder();
141140
sb.append(String.format("FROM %s:%s\n", DockerfileConfig.getBaseImageName(),
142141
DockerfileConfig.getBaseImageVersion()));
143142
sb.append("COPY /jdk /jdk\n");
144143
sb.append("ENV JAVA_HOME=/jdk\n");
145144

146-
sb.append(String.format("RUN groupadd --gid %s %s \n", gid, groupName));
147-
sb.append(String.format("RUN useradd --uid %s --gid %s %s \n", uid, gid, userName));
148-
sb.append(String.format("USER %s \n", userName));
145+
if (!IS_PODMAN) { // only needed for docker
146+
String uid = getId("-u");
147+
String gid = getId("-g");
148+
String userName = getId("-un");
149+
String groupName = getId("-gn");
150+
// Only needed when run as regular user. UID == 0 should already exist
151+
if (!ROOT_UID.equals(uid)) {
152+
sb.append(String.format("RUN groupadd --gid %s %s \n", gid, groupName));
153+
sb.append(String.format("RUN useradd --uid %s --gid %s %s \n", uid, gid, userName));
154+
sb.append(String.format("USER %s \n", userName));
155+
}
156+
}
149157

150158
sb.append("CMD [\"/bin/bash\"]\n");
151159

@@ -155,12 +163,17 @@ private static String generateCustomDockerfile(String uid, String gid,
155163

156164
private static Process startObservedContainer() throws Exception {
157165
DockerRunOptions opts = new DockerRunOptions(IMAGE_NAME, "/jdk/bin/java", "EventGeneratorLoop");
158-
opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/")
166+
opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/:z")
159167
.addJavaOpts("-cp", "/test-classes/")
160168
.addDockerOpts("--cap-add=SYS_PTRACE")
161169
.addDockerOpts("--name", CONTAINER_NAME)
162170
.addClassOptions("" + TIME_TO_RUN_CONTAINER_PROCESS);
163171

172+
if (IS_PODMAN) {
173+
// map the current userid to the one in the target namespace
174+
opts.addDockerOpts("--userns=keep-id");
175+
}
176+
164177
// avoid large Xmx
165178
opts.appendTestJavaOptions = false;
166179

@@ -190,4 +203,78 @@ private static String getId(String param) throws Exception {
190203
System.out.println("getId() " + param + " returning: " + result);
191204
return result;
192205
}
206+
207+
// pre: IS_PODMAN == true
208+
private static String getPodmanVersionStr() {
209+
if (!IS_PODMAN) {
210+
return null;
211+
}
212+
try {
213+
ProcessBuilder pb = new ProcessBuilder(Container.ENGINE_COMMAND, "--version");
214+
OutputAnalyzer out = new OutputAnalyzer(pb.start())
215+
.shouldHaveExitValue(0);
216+
String result = out.asLines().get(0);
217+
System.out.println(Container.ENGINE_COMMAND + " --version returning: " + result);
218+
return result;
219+
} catch (Exception e) {
220+
System.out.println(Container.ENGINE_COMMAND + " --version command failed. Returning null");
221+
return null;
222+
}
223+
}
224+
225+
private static PodmanVersion getPodmanVersion() {
226+
return PodmanVersion.fromVersionString(getPodmanVersionStr());
227+
}
228+
229+
private static class PodmanVersion implements Comparable<PodmanVersion> {
230+
private static final PodmanVersion DEFAULT = new PodmanVersion(0, 0, 0);
231+
private static final PodmanVersion VERSION_3_3_1 = new PodmanVersion(3, 3, 1);
232+
private final int major;
233+
private final int minor;
234+
private final int micro;
235+
236+
private PodmanVersion(int major, int minor, int micro) {
237+
this.major = major;
238+
this.minor = minor;
239+
this.micro = micro;
240+
}
241+
242+
@Override
243+
public int compareTo(PodmanVersion other) {
244+
if (this.major > other.major) {
245+
return 1;
246+
} else if (this.major < other.major) {
247+
return -1;
248+
} else { // equal major
249+
if (this.minor > other.minor) {
250+
return 1;
251+
} else if (this.minor < other.minor) {
252+
return -1;
253+
} else { // equal majors and minors
254+
if (this.micro > other.micro) {
255+
return 1;
256+
} else if (this.micro < other.micro) {
257+
return -1;
258+
} else {
259+
// equal majors, minors, micro
260+
return 0;
261+
}
262+
}
263+
}
264+
}
265+
266+
private static PodmanVersion fromVersionString(String version) {
267+
try {
268+
// Example 'podman version 3.2.1'
269+
String versNums = version.split("\\s+", 3)[2];
270+
String[] numbers = versNums.split("\\.", 3);
271+
return new PodmanVersion(Integer.parseInt(numbers[0]),
272+
Integer.parseInt(numbers[1]),
273+
Integer.parseInt(numbers[2]));
274+
} catch (Exception e) {
275+
System.out.println("Failed to parse podman version: " + version);
276+
return DEFAULT;
277+
}
278+
}
279+
}
193280
}

0 commit comments

Comments
 (0)