@@ -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