/* * (C) Copyright 2015 Kurento (http://kurento.org/) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.kurento.test.services; import static org.kurento.commons.PropertiesManager.getProperty; import static org.kurento.test.browser.WebRtcCandidateType.RELAY; import static org.kurento.test.browser.WebRtcCandidateType.SRFLX; import static org.kurento.test.config.TestConfiguration.AUTOSTART_FALSE_VALUE; import static org.kurento.test.config.TestConfiguration.AUTOSTART_TESTCLASS_VALUE; import static org.kurento.test.config.TestConfiguration.AUTOSTART_TESTSUITE_VALUE; import static org.kurento.test.config.TestConfiguration.AUTOSTART_TEST_VALUE; import static org.kurento.test.config.TestConfiguration.KMS_AUTOSTART_DEFAULT; import static org.kurento.test.config.TestConfiguration.KMS_AUTOSTART_PROP; import static org.kurento.test.config.TestConfiguration.KMS_DOCKER_IMAGE_FORCE_PULLING_DEFAULT; import static org.kurento.test.config.TestConfiguration.KMS_DOCKER_IMAGE_FORCE_PULLING_PROP; import static org.kurento.test.config.TestConfiguration.KMS_DOCKER_IMAGE_NAME_DEFAULT; import static org.kurento.test.config.TestConfiguration.KMS_DOCKER_IMAGE_NAME_PROP; import static org.kurento.test.config.TestConfiguration.KMS_DOCKER_S3_ACCESS_KEY_ID; import static org.kurento.test.config.TestConfiguration.KMS_DOCKER_S3_BUCKET_NAME; import static org.kurento.test.config.TestConfiguration.KMS_DOCKER_S3_HOSTNAME; import static org.kurento.test.config.TestConfiguration.KMS_DOCKER_S3_SECRET_ACCESS_KEY; import static org.kurento.test.config.TestConfiguration.KMS_GENERATE_RTP_PTS_STATS_PROPERTY; import static org.kurento.test.config.TestConfiguration.KMS_GST_PLUGINS_DEFAULT; import static org.kurento.test.config.TestConfiguration.KMS_LOGIN_PROP; import static org.kurento.test.config.TestConfiguration.KMS_LOG_PATH_DEFAULT; import static org.kurento.test.config.TestConfiguration.KMS_LOG_PATH_PROP; import static org.kurento.test.config.TestConfiguration.KMS_PASSWD_PROP; import static org.kurento.test.config.TestConfiguration.KMS_PEM_PROP; import static org.kurento.test.config.TestConfiguration.KMS_SCOPE_DEFAULT; import static org.kurento.test.config.TestConfiguration.KMS_SCOPE_DOCKER; import static org.kurento.test.config.TestConfiguration.KMS_SCOPE_PROP; import static org.kurento.test.config.TestConfiguration.KMS_SERVER_COMMAND_DEFAULT; import static org.kurento.test.config.TestConfiguration.KMS_SERVER_COMMAND_PROP; import static org.kurento.test.config.TestConfiguration.KMS_SERVER_DEBUG_DEFAULT; import static org.kurento.test.config.TestConfiguration.KMS_SERVER_DEBUG_PROP; import static org.kurento.test.config.TestConfiguration.KMS_WS_URI_DEFAULT; import static org.kurento.test.config.TestConfiguration.KMS_WS_URI_PROP; import static org.kurento.test.config.TestConfiguration.KMS_WS_URI_PROP_EXPORT; import static org.kurento.test.config.TestConfiguration.KSM_GST_PLUGINS_PROP; import static org.kurento.test.config.TestConfiguration.TEST_ICE_CANDIDATE_KMS_TYPE; import static org.kurento.test.config.TestConfiguration.TEST_ICE_CANDIDATE_SELENIUM_TYPE; import static org.kurento.test.config.TestConfiguration.TEST_ICE_SERVER_URL_PROPERTY; import static org.kurento.test.config.TestConfiguration.TEST_KMS_DNAT; import static org.kurento.test.config.TestConfiguration.TEST_KMS_DNAT_DEFAULT; import static org.kurento.test.config.TestConfiguration.TEST_KMS_TRANSPORT; import static org.kurento.test.config.TestConfiguration.TEST_SELENIUM_DNAT; import static org.kurento.test.config.TestConfiguration.TEST_SELENIUM_DNAT_DEFAULT; import static org.kurento.test.services.TestService.TestServiceScope.EXTERNAL; import static org.kurento.test.services.TestService.TestServiceScope.TEST; import static org.kurento.test.services.TestService.TestServiceScope.TESTCLASS; import static org.kurento.test.services.TestService.TestServiceScope.TESTSUITE; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.Writer; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import javax.websocket.ClientEndpoint; import javax.websocket.ClientEndpointConfig; import javax.websocket.CloseReason; import javax.websocket.ContainerProvider; import javax.websocket.DeploymentException; import javax.websocket.Endpoint; import javax.websocket.EndpointConfig; import javax.websocket.OnClose; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.WebSocketContainer; import org.apache.commons.io.FileUtils; import org.kurento.client.ErrorEvent; import org.kurento.client.EventListener; import org.kurento.client.KurentoClient; import org.kurento.client.MediaPipeline; import org.kurento.client.ObjectCreatedEvent; import org.kurento.commons.exception.KurentoException; import org.kurento.test.base.KurentoTest; import org.kurento.test.config.TestConfiguration; import org.kurento.test.docker.Docker; import org.kurento.test.utils.Shell; import org.kurento.test.utils.SshConnection; import com.github.dockerjava.api.command.CreateContainerCmd; import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.api.model.AccessMode; import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.Volume; import com.github.dockerjava.api.model.VolumesFrom; import com.github.dockerjava.core.command.PullImageResultCallback; import com.google.common.io.CharStreams; import freemarker.template.Configuration; import freemarker.template.Template; /** * Kurento Media Server service. * * @author Boni Garcia (bgarcia@gsyc.es) * @since 6.1.1 */ public class KmsService extends TestService { // FIXME replace with a registration mechanism protected static String monitoredDockerContainerName; protected String dockerContainerName = "kms"; protected SshConnection remoteKmsSshConnection; protected Path workspace; protected String wsUri; protected boolean isKmsRemote; protected boolean isKmsDocker; protected boolean isKmsStarted; protected String registrarUri; protected String registrarLocalAddress = "127.0.0.1"; protected String kmsLoginProp; protected String kmsPasswdProp; protected String kmsPemProp; protected String kmsAutostartProp; protected String kmsAutostartDefault; protected String kmsWsUriProp; protected String kmsWsUriExportProp; protected String kmsScopeProp; protected String kmsScopeDefault; protected KurentoClient kurentoClient; public KmsService(String wsUri) { this(); setWsUri(wsUri); } public KmsService() { this.kmsLoginProp = KMS_LOGIN_PROP; this.kmsPasswdProp = KMS_PASSWD_PROP; this.kmsPemProp = KMS_PEM_PROP; this.kmsAutostartProp = KMS_AUTOSTART_PROP; this.kmsAutostartDefault = KMS_AUTOSTART_DEFAULT; this.kmsWsUriProp = KMS_WS_URI_PROP; this.kmsWsUriExportProp = KMS_WS_URI_PROP_EXPORT; this.kmsScopeProp = KMS_SCOPE_PROP; this.kmsScopeDefault = KMS_SCOPE_DEFAULT; setWsUri(getProperty(kmsWsUriProp, KMS_WS_URI_DEFAULT)); } public KmsService(String kmsLoginProp, String kmsPasswdProp, String kmsPemProp, String kmsAutostartProp, String kmsWsUriProp, String kmsWsUriExportProp, String kmsScopeProp, String kmsScopeDefault) { this.kmsLoginProp = kmsLoginProp; this.kmsPasswdProp = kmsPasswdProp; this.kmsPemProp = kmsPemProp; this.kmsAutostartProp = kmsAutostartProp; this.kmsWsUriProp = kmsWsUriProp; this.kmsWsUriExportProp = kmsWsUriExportProp; this.kmsScopeProp = kmsScopeProp; this.kmsScopeDefault = kmsScopeDefault; setWsUri(getProperty(kmsWsUriProp, KMS_WS_URI_DEFAULT)); } @Override public void start() { super.start(); if (wsUri == null) { log.warn("WS URI is null, will not start"); isKmsStarted = false; return; } isKmsRemote = !wsUri.contains("localhost") && !wsUri.contains("127.0.0.1") && !isKmsDocker; isKmsDocker = KMS_SCOPE_DOCKER.equals(getProperty(kmsScopeProp, kmsScopeDefault)); // Assertion: if KMS remote, credentials should be available String kmsLogin = getProperty(kmsLoginProp); String kmsPasswd = getProperty(kmsPasswdProp); String kmsPem = getProperty(kmsPemProp); String kmsAutoStart = getProperty(kmsAutostartProp, kmsAutostartDefault); if (isKmsRemote && kmsLogin == null && (kmsPem == null || kmsPasswd == null)) { throw new KurentoException( "Bad test parameters: " + kmsAutostartProp + "=" + kmsAutoStart + " and " + kmsWsUriProp + "=" + wsUri + ". Remote KMS should be started but its credentials are not present: " + kmsLoginProp + "=" + kmsLogin + ", " + kmsPasswdProp + "=" + kmsPasswd + ", " + kmsPemProp + "=" + kmsPem); } // Assertion: if local or remote KMS, port should be available if (!isKmsDocker && !isKmsRemote && !isFreePort(wsUri)) { throw new KurentoException("KMS cannot be started in URI: " + wsUri + ". Port is not free"); } if (isKmsDocker) { log.debug("Starting KMS dockerized"); Docker dockerClient = Docker.getSingleton(); if (dockerClient.isRunningInContainer()) { setDockerContainerName(dockerClient.getContainerName() + getDockerContainerNameSuffix() + "-" + KurentoTest.getTestClassName() + "-" + +new Random().nextInt(3000)); } } else { log.debug("Starting KMS with URI: {}", wsUri); try { workspace = Files.createTempDirectory("kurento-test"); } catch (IOException e) { throw new KurentoException("Exception creating temporal folder", e); } log.trace("Local folder to store temporal files: {}", workspace); if (isKmsRemote) { String remoteKmsStr = wsUri.substring(wsUri.indexOf("//") + 2, wsUri.lastIndexOf(":")); log.debug("Using remote KMS at {}", remoteKmsStr); remoteKmsSshConnection = new SshConnection(remoteKmsStr, kmsLogin, kmsPasswd, kmsPem); if (kmsPem != null) { remoteKmsSshConnection.setPem(kmsPem); } remoteKmsSshConnection.start(); remoteKmsSshConnection.createTmpFolder(); } createKurentoConf(); } if (isKmsRemote && !kmsAutoStart.equals(AUTOSTART_FALSE_VALUE)) { String[] filesToBeCopied = { "kurento.conf.json", "kurento.sh" }; for (String s : filesToBeCopied) { remoteKmsSshConnection.scp(workspace + File.separator + s, remoteKmsSshConnection.getTmpFolder() + File.separator + s); } remoteKmsSshConnection.runAndWaitCommand("chmod", "+x", remoteKmsSshConnection.getTmpFolder() + File.separator + "kurento.sh"); } startKms(); waitForKms(); } @Override public void stop() { super.stop(); // Close Kurento client closeKurentoClient(); // Stop KMS stopKms(); // Retrieve logs try { retrieveLogs(); } catch (IOException e) { log.warn("Exception retrieving KMS logs", e); } if (isKmsDocker) { try { Docker.getSingleton().removeContainer(dockerContainerName); log.debug("*** Only for debugging: Docker.getSingleton().removeContainer({})", dockerContainerName); } catch (Throwable name) { log.error(" +++ Only for debugging: Exception on Docker.getSingleton().removeContainer({})", dockerContainerName); } } log.debug("+++ Only for debugging: After removeContainer {}", dockerContainerName); // Delete temporal folder and content if (!isKmsDocker) { try { deleteFolderAndContent(workspace); } catch (IOException e) { log.warn("Exception deleting temporal folder {}", workspace, e); } } log.debug("+++ Only for debugging: End of KmsService.stop() for: {}", dockerContainerName); } @Override public TestServiceScope getScope() { TestServiceScope scope = TESTSUITE; String kmsAutostart = getProperty(kmsAutostartProp, kmsAutostartDefault); switch (kmsAutostart) { case AUTOSTART_FALSE_VALUE: scope = EXTERNAL; break; case AUTOSTART_TEST_VALUE: scope = TEST; break; case AUTOSTART_TESTCLASS_VALUE: scope = TESTCLASS; break; case AUTOSTART_TESTSUITE_VALUE: scope = TESTSUITE; break; default: throw new IllegalArgumentException("Unknown autostart value " + kmsAutostart); } return scope; } protected String getDockerContainerNameSuffix() { return "_kms"; } protected String getDockerLogSuffix() { return "-kms"; } private boolean isFreePort(String wsUri) { try { URI wsUrl = new URI(wsUri); String result = Shell.runAndWait("/bin/bash", "-c", "nc -z " + wsUrl.getHost() + " " + wsUrl.getPort() + "; echo $?"); if (result.trim().equals("0")) { log.warn("Port " + wsUrl.getPort() + " is used. Maybe another KMS instance is running in this port"); return false; } } catch (URISyntaxException e) { log.warn("WebSocket URI {} is malformed: " + e.getMessage(), wsUri); } return true; } private void createKurentoConf() { Map<String, Object> data = new HashMap<String, Object>(); try { URI wsAsUri = new URI(wsUri); int port = wsAsUri.getPort(); String path = wsAsUri.getPath(); data.put("wsPort", String.valueOf(port)); data.put("wsPath", path.substring(1)); data.put("registrar", registrarUri); data.put("registrarLocalAddress", registrarLocalAddress); } catch (URISyntaxException e) { throw new KurentoException("Invalid ws uri: " + wsUri); } data.put("gstPlugins", getGstPlugins()); data.put("debugOptions", getDebugOptions()); data.put("serverCommand", getServerCommand()); data.put("workspace", getKmsLogPath()); Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); cfg.setClassForTemplateLoading(this.getClass(), "/templates/"); createFileFromTemplate(cfg, data, "kurento.conf.json"); createFileFromTemplate(cfg, data, "kurento.sh"); Shell.runAndWait("chmod", "+x", workspace + File.separator + "kurento.sh"); } private void startKms() { String kmsLogPath = getKmsLogPath(); if (isKmsRemote) { remoteKmsSshConnection.runAndWaitCommand("sh", "-c", kmsLogPath + "kurento.sh > /dev/null"); log.debug("Remote KMS started in URI {}", wsUri); } else if (isKmsDocker) { startDockerizedKms(); } else { Shell.run("sh", "-c", kmsLogPath + "kurento.sh"); log.debug("Local KMS started in URI {}", wsUri); } isKmsStarted = true; } private void waitForKms() { long initTime = System.nanoTime(); @ClientEndpoint class WebSocketClient extends Endpoint { @OnClose @Override public void onClose(Session session, CloseReason closeReason) { } @OnOpen @Override public void onOpen(Session session, EndpointConfig config) { } } if (wsUri != null) { WebSocketContainer container = ContainerProvider.getWebSocketContainer(); final int retries = 600; final int waitTime = 100; for (int i = 0; i < retries; i++) { try { log.debug("({}) Wait for KMS: {}. Container: {}", i, wsUri, container); Session wsSession = container.connectToServer(new WebSocketClient(), ClientEndpointConfig.Builder.create().build(), new URI(wsUri)); wsSession.close(); double time = (System.nanoTime() - initTime) / (double) 1000000; log.debug("Connected to KMS in " + String.format("%3.2f", time) + " milliseconds"); return; } catch (DeploymentException | IOException | URISyntaxException e) { try { log.warn("Exception while waiting for KMS: {}. {}", wsUri, e.getMessage()); Thread.sleep(waitTime); } catch (InterruptedException e1) { e1.printStackTrace(); } } } throw new KurentoException( "Timeout of " + retries * waitTime + " millis waiting for KMS " + wsUri); } else { try { Thread.sleep(1000); } catch (InterruptedException e) { log.error("InterruptedException {}", e.getMessage()); } } } private void startDockerizedKms() { Docker dockerClient = Docker.getSingleton(); String kmsImageName = getProperty(KMS_DOCKER_IMAGE_NAME_PROP, KMS_DOCKER_IMAGE_NAME_DEFAULT); boolean forcePulling = getProperty(KMS_DOCKER_IMAGE_FORCE_PULLING_PROP, KMS_DOCKER_IMAGE_FORCE_PULLING_DEFAULT); if (!dockerClient.existsImage(kmsImageName) || forcePulling) { log.debug("Pulling KMS image {} ... please wait", kmsImageName); dockerClient.getClient().pullImageCmd(kmsImageName).exec(new PullImageResultCallback()) .awaitSuccess(); log.debug("KMS image {} pulled", kmsImageName); } if (dockerClient.existsContainer(dockerContainerName)) { log.warn("Trying to create a new container named '" + dockerContainerName + "' but it already exist. Stopping and removing existing one and creating it again."); dockerClient.stopAndRemoveContainer(dockerContainerName); } log.debug("Starting KMS container...{}", dockerContainerName); // Check S3 properties String s3BucketName = getProperty(KMS_DOCKER_S3_BUCKET_NAME); String s3AccessKeyId = getProperty(KMS_DOCKER_S3_ACCESS_KEY_ID); String s3SecretAccessKey = getProperty(KMS_DOCKER_S3_SECRET_ACCESS_KEY); String s3Hostname = getProperty(KMS_DOCKER_S3_HOSTNAME); Boolean kmsDnat = false; if (getProperty(TEST_KMS_DNAT) != null && getProperty(TEST_KMS_DNAT, TEST_KMS_DNAT_DEFAULT)) { kmsDnat = true; } Boolean seleniumDnat = false; if (getProperty(TEST_SELENIUM_DNAT) != null && getProperty(TEST_SELENIUM_DNAT, TEST_SELENIUM_DNAT_DEFAULT)) { seleniumDnat = true; } String kmsCandidateType = getProperty(TEST_ICE_CANDIDATE_KMS_TYPE); String seleniumCandidateType = getProperty(TEST_ICE_CANDIDATE_SELENIUM_TYPE); // Check Stun properties String kmsStunIp = getProperty(TestConfiguration.KMS_STUN_IP_PROPERTY); String kmsStunPort = getProperty(TestConfiguration.KMS_STUN_PORT_PROPERTY); CreateContainerCmd createContainerCmd; if (kmsDnat && seleniumDnat && RELAY.toString().toUpperCase().equals(kmsCandidateType) && SRFLX.toString().toUpperCase().equals(seleniumCandidateType)) { // Use Turn for KMS String kmsTurnIp = getProperty(TEST_ICE_SERVER_URL_PROPERTY); log.debug("Turn Server {}", kmsTurnIp); createContainerCmd = dockerClient.getClient().createContainerCmd(kmsImageName).withName(dockerContainerName) .withEnv("GST_DEBUG=" + getDebugOptions(), "S3_ACCESS_BUCKET_NAME=" + s3BucketName, "S3_ACCESS_KEY_ID=" + s3AccessKeyId, "S3_SECRET_ACCESS_KEY=" + s3SecretAccessKey, "S3_HOSTNAME=" + s3Hostname, "KMS_TURN_URL=" + kmsTurnIp, "KURENTO_GENERATE_RTP_PTS_STATS=" + getKurentoGenerateRtpPtsStats()) .withCmd("--gst-debug-no-color").withVolumes(new Volume("/var/run/docker.sock")); } else { if (kmsDnat && seleniumDnat && RELAY.toString().toUpperCase().equals(seleniumCandidateType) && SRFLX.toString().toUpperCase().equals(kmsCandidateType)) { // Change kmsStunIp by turn values kmsStunIp = getProperty(TEST_ICE_SERVER_URL_PROPERTY).split(":")[1]; kmsStunPort = "3478"; } if (kmsStunIp == null) { kmsStunIp = ""; } if (kmsStunPort == null) { kmsStunPort = ""; } log.debug("Stun Server {}:{}", kmsStunIp, kmsStunPort); createContainerCmd = dockerClient.getClient().createContainerCmd(kmsImageName).withName(dockerContainerName) .withEnv("GST_DEBUG=" + getDebugOptions(), "S3_ACCESS_BUCKET_NAME=" + s3BucketName, "S3_ACCESS_KEY_ID=" + s3AccessKeyId, "S3_SECRET_ACCESS_KEY=" + s3SecretAccessKey, "S3_HOSTNAME=" + s3Hostname, "KMS_STUN_IP=" + kmsStunIp, "KMS_STUN_PORT=" + kmsStunPort, "KURENTO_GENERATE_RTP_PTS_STATS=" + getKurentoGenerateRtpPtsStats()) .withCmd("--gst-debug-no-color").withVolumes(new Volume("/var/run/docker.sock")); } if (dockerClient.isRunningInContainer()) { createContainerCmd.withVolumesFrom(new VolumesFrom(dockerClient.getContainerId())); } else { String testFilesPath = KurentoTest.getTestFilesDiskPath(); Volume volume = new Volume(testFilesPath); String targetPath = Paths.get(KurentoTest.getDefaultOutputFolder().toURI()).toAbsolutePath().toString(); Volume volumeTest = new Volume(targetPath); createContainerCmd.withVolumes(volume, volumeTest).withBinds( new Bind(testFilesPath, volume, AccessMode.ro), new Bind(targetPath, volumeTest, AccessMode.rw)); } String kmsAddress = ""; if (kmsDnat) { log.debug("Set network, for kms, as none"); createContainerCmd.withNetworkMode("none"); Map<String, String> labels = new HashMap<String, String>(); labels.put("KurentoDnat", "true"); labels.put("Transport", getProperty(TEST_KMS_TRANSPORT)); kmsAddress = dockerClient.generateIpAddressForContainer(); labels.put("IpAddress", kmsAddress); createContainerCmd.withLabels(labels); CreateContainerResponse kmsContainer = createContainerCmd.exec(); dockerClient.getClient().startContainerCmd(kmsContainer.getId()).exec(); } else { CreateContainerResponse kmsContainer = createContainerCmd.exec(); dockerClient.getClient().startContainerCmd(kmsContainer.getId()).exec(); kmsAddress = dockerClient.inspectContainer(dockerContainerName).getNetworkSettings().getIpAddress(); } setWsUri("ws://" + kmsAddress + ":8888/kurento"); log.debug("Dockerized KMS started in URI {}", wsUri); } public String getKmsLogPath() { String kmsAutoStart = getProperty(kmsAutostartProp, kmsAutostartDefault); return kmsAutoStart.equals(AUTOSTART_FALSE_VALUE) ? getProperty(KMS_LOG_PATH_PROP, KMS_LOG_PATH_DEFAULT) : isKmsRemote ? remoteKmsSshConnection.getTmpFolder() + File.separator : workspace + File.separator; } private void createFileFromTemplate(Configuration cfg, Map<String, Object> data, String filename) { try { Template template = cfg.getTemplate(filename + ".ftl"); File file = new File(workspace + File.separator + filename); Writer writer = new FileWriter(file); template.process(data, writer); writer.flush(); writer.close(); log.trace("Created file '{}'", file.getAbsolutePath()); } catch (Exception e) { throw new KurentoException("Exception while creating file from template", e); } } public void retrieveLogs() throws IOException { File targetFolder = KurentoTest.getDefaultOutputFolder(); String kmsLogsPath = getKmsLogPath(); Path defaultOutput = Paths.get(targetFolder.toURI()); if (!Files.exists(defaultOutput)) { Files.createDirectories(defaultOutput); } if (isKmsStarted) { kmsLogsPath += "logs/"; } String testMethodName = KurentoTest.getSimpleTestName(); if (isKmsDocker) { Docker.getSingleton().downloadLog(dockerContainerName, Paths .get(targetFolder.getAbsolutePath(), testMethodName + getDockerLogSuffix() + ".log")); } else if (isKmsRemote) { if (!remoteKmsSshConnection.isStarted()) { remoteKmsSshConnection.start(); } log.debug("Copying KMS logs located on {} from remote host {} to {}", kmsLogsPath, remoteKmsSshConnection.getConnection(), targetFolder); List<String> remoteLogFiles = remoteKmsSshConnection.listFiles(kmsLogsPath, true, false); for (String remoteLogFile : remoteLogFiles) { String localLogFile = targetFolder + "/" + testMethodName + "-" + remoteLogFile.substring(remoteLogFile.lastIndexOf("/") + 1); remoteKmsSshConnection.getFile(localLogFile, remoteLogFile); KurentoTest.addLogFile(new File(localLogFile)); log.debug("Log file: {}", localLogFile); } } else { File directory = new File(kmsLogsPath); if (directory.isDirectory()) { log.debug("Copying KMS logs from local path {} to {}", kmsLogsPath, targetFolder); Collection<File> logFiles = FileUtils.listFiles(directory, null, true); for (File logFile : logFiles) { File destFile = new File(targetFolder, testMethodName + "-" + logFile.getName()); try { FileUtils.copyFile(logFile, destFile); KurentoTest.addLogFile(destFile); log.debug("Log file: {}", destFile); } catch (Throwable e) { log.warn("Exception copy KMS file {} {}", e.getClass(), e.getMessage()); } } } else { log.warn("Path {} is not a directory", directory); } } } public void stopKms() { if (isKmsDocker) { Docker.getSingleton().stopContainer(dockerContainerName); } else { killKmsProcesses(); if (isKmsRemote) { remoteKmsSshConnection.stop(); } } isKmsStarted = false; } private void killKmsProcesses() { int numKmsProcesses = 0; // Max timeout waiting kms ending: 5 seconds long timeout = System.currentTimeMillis() + 5000; do { // If timeout, break the loop if (System.currentTimeMillis() > timeout) { break; } // Sending SIGTERM signal to KMS process kmsSigTerm(); // Wait 100 msec to order kms termination try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } numKmsProcesses = countKmsProcesses(); } while (numKmsProcesses > 0); if (numKmsProcesses > 0) { // If at this point there is still kms process (after trying to // kill it with SIGTERM during 5 seconds), we send the SIGKILL // signal to the process kmsSigKill(); } } private void kmsSigTerm() { log.trace("Sending SIGTERM to KMS process"); if (isKmsRemote) { String kmsPid = remoteKmsSshConnection.execAndWaitCommandNoBr("cat", remoteKmsSshConnection.getTmpFolder() + "/kms-pid"); remoteKmsSshConnection.runAndWaitCommand("kill", kmsPid); } else { Shell.runAndWait("sh", "-c", "kill `cat " + workspace + File.separator + "kms-pid`"); } } private void kmsSigKill() { log.trace("Sending SIGKILL to KMS process"); if (isKmsRemote) { String kmsPid = remoteKmsSshConnection.execAndWaitCommandNoBr("cat", remoteKmsSshConnection.getTmpFolder() + "/kms-pid"); remoteKmsSshConnection.runAndWaitCommand("sh", "-c", "kill -9 " + kmsPid); } else { Shell.runAndWait("sh", "-c", "kill -9 `cat " + workspace + File.separator + "kms-pid`"); } } private int countKmsProcesses() { int result = 0; try { // This command counts number of process (given its PID, stored in // kms-pid file) if (isKmsRemote) { String kmsPid = remoteKmsSshConnection.execAndWaitCommandNoBr("cat", remoteKmsSshConnection.getTmpFolder() + "/kms-pid"); result = Integer.parseInt(remoteKmsSshConnection .execAndWaitCommandNoBr("ps --pid " + kmsPid + " --no-headers | wc -l")); } else { String[] command = { "sh", "-c", "ps --pid `cat " + workspace + File.separator + "kms-pid` --no-headers | wc -l" }; Process countKms = Runtime.getRuntime().exec(command); String stringFromStream = CharStreams.toString(new InputStreamReader(countKms.getInputStream(), "UTF-8")); result = Integer.parseInt(stringFromStream.trim()); } } catch (IOException e) { log.warn("Exception counting KMS processes", e); } return result; } private void deleteFolderAndContent(Path folder) throws IOException { if (folder != null) { Files.walkFileTree(folder, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.delete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { Files.delete(dir); return FileVisitResult.CONTINUE; } }); } } public synchronized void setDockerContainerName(String containerName) { dockerContainerName = containerName; if (monitoredDockerContainerName == null) { monitoredDockerContainerName = dockerContainerName; } } private String getServerCommand() { return getProperty(KMS_SERVER_COMMAND_PROP, KMS_SERVER_COMMAND_DEFAULT); } private String getGstPlugins() { return getProperty(KSM_GST_PLUGINS_PROP, KMS_GST_PLUGINS_DEFAULT); } private String getDebugOptions() { return getProperty(KMS_SERVER_DEBUG_PROP, KMS_SERVER_DEBUG_DEFAULT); } private String getKurentoGenerateRtpPtsStats() { String path = getProperty(KMS_GENERATE_RTP_PTS_STATS_PROPERTY, KurentoTest.getDefaultOutputTestPath()); log.debug("{} = {}", KMS_GENERATE_RTP_PTS_STATS_PROPERTY, path); return path; } public KurentoClient getKurentoClient() { if (kurentoClient == null && wsUri != null) { kurentoClient = createKurentoClient(); kurentoClient.getServerManager() .addObjectCreatedListener(new EventListener<ObjectCreatedEvent>() { @Override public void onEvent(ObjectCreatedEvent event) { if (event instanceof MediaPipeline) { MediaPipeline mp = (MediaPipeline) event; mp.addErrorListener(new EventListener<ErrorEvent>() { @Override public void onEvent(ErrorEvent event) { String msgException = "Error in KMS: " + event.getDescription() + "; Type: " + event.getType() + "; Error Code: " + event.getErrorCode(); log.error(msgException); throw new KurentoException(msgException); } }); } } }); } return kurentoClient; } public KurentoClient createKurentoClient() { return KurentoClient.create(wsUri); } public void closeKurentoClient() { if (kurentoClient != null) { kurentoClient.destroy(); kurentoClient = null; } } public String getWsUri() { return wsUri; } public void setWsUri(String wsUri) { if (wsUri != null) { System.setProperty(kmsWsUriExportProp, wsUri); } this.wsUri = wsUri; } public void setRegistrarUri(String registrarUri) { this.registrarUri = registrarUri; } public void setRegistrarLocalAddress(String registrarLocalAddress) { this.registrarLocalAddress = registrarLocalAddress; } public boolean isKmsStarted() { return isKmsStarted; } // returns the name of the first container public static String getMonitoredDockerContainerName() { return monitoredDockerContainerName; } }