8227122: [TESTBUG] Create Docker sidecar test cases
Created test cases for container sidecar pattern Reviewed-by: bobv, sgehwolf
This commit is contained in:
parent
d9c3c6f9bf
commit
94c38c4cc1
60
test/hotspot/jtreg/containers/docker/EventGeneratorLoop.java
Normal file
60
test/hotspot/jtreg/containers/docker/EventGeneratorLoop.java
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.Description;
|
||||
import jdk.jfr.Label;
|
||||
|
||||
|
||||
// This class generates simple event in a loop
|
||||
// for a specified time.
|
||||
// Pass the time in seconds as a parameter.
|
||||
public class EventGeneratorLoop {
|
||||
|
||||
@Label("SimpleEvent")
|
||||
@Description("Simple custom event")
|
||||
static class SimpleEvent extends Event {
|
||||
@Label("Message")
|
||||
String msg;
|
||||
|
||||
@Label("Count")
|
||||
int count;
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if ((args.length < 1) || (args[0] == null)) {
|
||||
throw new IllegalArgumentException("Expecting one argument: time to run (seconds)");
|
||||
}
|
||||
int howLong = Integer.parseInt(args[0]);
|
||||
|
||||
for (int i=0; i < howLong; i++) {
|
||||
SimpleEvent ev = new SimpleEvent();
|
||||
ev.msg = "Hello";
|
||||
ev.count = i;
|
||||
ev.commit();
|
||||
|
||||
try { Thread.sleep(1000); } catch (InterruptedException e) {}
|
||||
System.out.print(".");
|
||||
}
|
||||
}
|
||||
}
|
231
test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java
Normal file
231
test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test JCMD with side car pattern.
|
||||
* Sidecar is a common pattern used in the cloud environments for monitoring
|
||||
* and other uses. In side car pattern the main application/service container
|
||||
* is paired with a sidecar container by sharing certain aspects of container
|
||||
* namespace such as PID namespace, specific sub-directories, IPC and more.
|
||||
* @requires docker.support
|
||||
* @library /test/lib
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.management
|
||||
* jdk.jartool/sun.tools.jar
|
||||
* @build EventGeneratorLoop
|
||||
* @run driver TestJcmdWithSideCar
|
||||
*/
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import jdk.test.lib.Container;
|
||||
import jdk.test.lib.Utils;
|
||||
import jdk.test.lib.containers.docker.Common;
|
||||
import jdk.test.lib.containers.docker.DockerRunOptions;
|
||||
import jdk.test.lib.containers.docker.DockerTestUtils;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
|
||||
public class TestJcmdWithSideCar {
|
||||
private static final String IMAGE_NAME = Common.imageName("jfr-jcmd");
|
||||
private static final int TIME_TO_RUN_MAIN_PROCESS = (int) (30 * Utils.TIMEOUT_FACTOR); // seconds
|
||||
private static final String MAIN_CONTAINER_NAME = "test-container-main";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (!DockerTestUtils.canTestDocker()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DockerTestUtils.buildJdkDockerImage(IMAGE_NAME, "Dockerfile-BasicTest", "jdk-docker");
|
||||
|
||||
try {
|
||||
// Start the loop process in the "main" container, then run test cases
|
||||
// using a sidecar container.
|
||||
DockerThread t = startMainContainer();
|
||||
|
||||
waitForMainContainerToStart(500, 10);
|
||||
t.checkForErrors();
|
||||
|
||||
OutputAnalyzer jcmdOut = testCase01();
|
||||
long mainProcPid = findProcess(jcmdOut, "EventGeneratorLoop");
|
||||
|
||||
t.assertIsAlive();
|
||||
testCase02(mainProcPid);
|
||||
|
||||
// JCMD does not work in sidecar configuration, except for "jcmd -l".
|
||||
// Including this test case to assist in reproduction of the problem.
|
||||
// t.assertIsAlive();
|
||||
// testCase03(mainProcPid);
|
||||
|
||||
t.join(TIME_TO_RUN_MAIN_PROCESS * 1000);
|
||||
t.checkForErrors();
|
||||
} finally {
|
||||
DockerTestUtils.removeDockerImage(IMAGE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Run "jcmd -l" in a sidecar container and find a process that runs EventGeneratorLoop
|
||||
private static OutputAnalyzer testCase01() throws Exception {
|
||||
return runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jcmd", "-l")
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldContain("sun.tools.jcmd.JCmd")
|
||||
.shouldContain("EventGeneratorLoop");
|
||||
}
|
||||
|
||||
// run jhsdb jinfo <PID> (jhsdb uses PTRACE)
|
||||
private static void testCase02(long pid) throws Exception {
|
||||
runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jhsdb", "jinfo", "--pid", "" + pid)
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldContain("Java System Properties")
|
||||
.shouldContain("VM Flags");
|
||||
}
|
||||
|
||||
// test jcmd with some commands (help, start JFR recording)
|
||||
// JCMD will use signal mechanism and Unix Socket
|
||||
private static void testCase03(long pid) throws Exception {
|
||||
runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jcmd", "" + pid, "help")
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldContain("VM.version");
|
||||
runSideCar(MAIN_CONTAINER_NAME, "/jdk/bin/jcmd", "" + pid, "JFR.start")
|
||||
.shouldHaveExitValue(0)
|
||||
.shouldContain("Started recording");
|
||||
}
|
||||
|
||||
private static DockerThread startMainContainer() throws Exception {
|
||||
// start "main" container (the observee)
|
||||
DockerRunOptions opts = commonDockerOpts("EventGeneratorLoop");
|
||||
opts.addDockerOpts("--cap-add=SYS_PTRACE")
|
||||
.addDockerOpts("--name", MAIN_CONTAINER_NAME)
|
||||
.addDockerOpts("-v", "/tmp")
|
||||
.addJavaOpts("-XX:+UsePerfData")
|
||||
.addClassOptions("" + TIME_TO_RUN_MAIN_PROCESS);
|
||||
DockerThread t = new DockerThread(opts);
|
||||
t.start();
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
private static void waitForMainContainerToStart(int delayMillis, int count) throws Exception {
|
||||
boolean started = false;
|
||||
for(int i=0; i < count; i++) {
|
||||
try {
|
||||
Thread.sleep(delayMillis);
|
||||
} catch (InterruptedException e) {}
|
||||
if (isMainContainerRunning()) {
|
||||
started = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!started) {
|
||||
throw new RuntimeException("Main container did not start");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isMainContainerRunning() throws Exception {
|
||||
OutputAnalyzer out =
|
||||
DockerTestUtils.execute(Container.ENGINE_COMMAND,
|
||||
"ps", "--no-trunc",
|
||||
"--filter", "name=" + MAIN_CONTAINER_NAME);
|
||||
return out.getStdout().contains(MAIN_CONTAINER_NAME);
|
||||
}
|
||||
|
||||
// JCMD relies on the attach mechanism (com.sun.tools.attach),
|
||||
// which in turn relies on JVMSTAT mechanism, which puts its mapped
|
||||
// buffers in /tmp directory (hsperfdata_<user>). Thus, in sidecar
|
||||
// we mount /tmp via --volumes-from from the main container.
|
||||
private static OutputAnalyzer runSideCar(String MAIN_CONTAINER_NAME, String whatToRun,
|
||||
String... args) throws Exception {
|
||||
List<String> cmd = new ArrayList<>();
|
||||
String[] command = new String[] {
|
||||
Container.ENGINE_COMMAND, "run",
|
||||
"--tty=true", "--rm",
|
||||
"--cap-add=SYS_PTRACE", "--sig-proxy=true",
|
||||
"--pid=container:" + MAIN_CONTAINER_NAME,
|
||||
"--volumes-from", MAIN_CONTAINER_NAME,
|
||||
IMAGE_NAME, whatToRun
|
||||
};
|
||||
|
||||
cmd.addAll(Arrays.asList(command));
|
||||
cmd.addAll(Arrays.asList(args));
|
||||
return DockerTestUtils.execute(cmd);
|
||||
}
|
||||
|
||||
private static long findProcess(OutputAnalyzer out, String name) throws Exception {
|
||||
List<String> l = out.asLines()
|
||||
.stream()
|
||||
.filter(s -> s.contains(name))
|
||||
.collect(Collectors.toList());
|
||||
if (l.isEmpty()) {
|
||||
throw new RuntimeException("Could not find matching process");
|
||||
}
|
||||
String psInfo = l.get(0);
|
||||
System.out.println("findProcess(): psInfo: " + psInfo);
|
||||
String pid = psInfo.substring(0, psInfo.indexOf(' '));
|
||||
System.out.println("findProcess(): pid: " + pid);
|
||||
return Long.parseLong(pid);
|
||||
}
|
||||
|
||||
private static DockerRunOptions commonDockerOpts(String className) {
|
||||
return new DockerRunOptions(IMAGE_NAME, "/jdk/bin/java", className)
|
||||
.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/")
|
||||
.addJavaOpts("-cp", "/test-classes/");
|
||||
}
|
||||
|
||||
|
||||
static class DockerThread extends Thread {
|
||||
DockerRunOptions runOpts;
|
||||
Exception exception;
|
||||
|
||||
DockerThread(DockerRunOptions opts) {
|
||||
runOpts = opts;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
DockerTestUtils.dockerRunJava(runOpts);
|
||||
} catch (Exception e) {
|
||||
exception = e;
|
||||
}
|
||||
}
|
||||
|
||||
public void assertIsAlive() throws Exception {
|
||||
if (!isAlive()) {
|
||||
throw new RuntimeException("DockerThread stopped unexpectedly");
|
||||
}
|
||||
}
|
||||
|
||||
public void checkForErrors() throws Exception {
|
||||
if (exception != null) {
|
||||
throw new RuntimeException("DockerThread threw exception"
|
||||
+ exception.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -189,7 +189,7 @@ public class DockerTestUtils {
|
||||
/**
|
||||
* Build the docker command to run java inside a container
|
||||
*
|
||||
* @param DockerRunOptions optins for running docker
|
||||
* @param DockerRunOptions options for running docker
|
||||
*
|
||||
* @return command
|
||||
* @throws Exception
|
||||
@ -222,7 +222,7 @@ public class DockerTestUtils {
|
||||
/**
|
||||
* Run Java inside the docker image with specified parameters and options.
|
||||
*
|
||||
* @param DockerRunOptions optins for running docker
|
||||
* @param DockerRunOptions options for running docker
|
||||
*
|
||||
* @return output of the run command
|
||||
* @throws Exception
|
||||
@ -235,7 +235,7 @@ public class DockerTestUtils {
|
||||
/**
|
||||
* Remove docker image
|
||||
*
|
||||
* @param DockerRunOptions optins for running docker
|
||||
* @param DockerRunOptions options for running docker
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void removeDockerImage(String imageNameAndTag) throws Exception {
|
||||
|
Loading…
Reference in New Issue
Block a user