mirror of https://github.com/OpenVidu/openvidu.git
openvidu-server: DockerManager refactoring
parent
ff53377925
commit
c282d53349
|
@ -48,7 +48,7 @@ public class OpenViduException extends JsonRpcErrorException {
|
|||
SIGNAL_FORMAT_INVALID_ERROR_CODE(600), SIGNAL_TO_INVALID_ERROR_CODE(601),
|
||||
SIGNAL_MESSAGE_INVALID_ERROR_CODE(602),
|
||||
|
||||
RECORDING_ENABLED_BUT_DOCKER_NOT_FOUND(709), RECORDING_PATH_NOT_VALID(708), RECORDING_FILE_EMPTY_ERROR(707),
|
||||
DOCKER_NOT_FOUND(709), RECORDING_PATH_NOT_VALID(708), RECORDING_FILE_EMPTY_ERROR(707),
|
||||
RECORDING_DELETE_ERROR_CODE(706), RECORDING_LIST_ERROR_CODE(705), RECORDING_STOP_ERROR_CODE(704),
|
||||
RECORDING_START_ERROR_CODE(703), RECORDING_REPORT_ERROR_CODE(702), RECORDING_COMPLETION_ERROR_CODE(701);
|
||||
|
||||
|
|
|
@ -257,7 +257,7 @@ public class OpenViduServer implements JsonRpcConfigurer {
|
|||
this.recordingManager().initializeRecordingManager();
|
||||
} catch (OpenViduException e) {
|
||||
String finalErrorMessage = "";
|
||||
if (e.getCodeValue() == Code.RECORDING_ENABLED_BUT_DOCKER_NOT_FOUND.getValue()) {
|
||||
if (e.getCodeValue() == Code.DOCKER_NOT_FOUND.getValue()) {
|
||||
finalErrorMessage = "Error connecting to Docker daemon. Enabling OpenVidu recording module requires Docker";
|
||||
} else if (e.getCodeValue() == Code.RECORDING_PATH_NOT_VALID.getValue()) {
|
||||
finalErrorMessage = "Error initializing recording path \""
|
||||
|
|
|
@ -30,18 +30,8 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import com.github.dockerjava.api.DockerClient;
|
||||
import com.github.dockerjava.api.command.CreateContainerCmd;
|
||||
import com.github.dockerjava.api.command.CreateContainerResponse;
|
||||
import com.github.dockerjava.api.command.ExecCreateCmdResponse;
|
||||
import com.github.dockerjava.api.exception.ConflictException;
|
||||
import com.github.dockerjava.api.exception.NotFoundException;
|
||||
import com.github.dockerjava.api.model.Bind;
|
||||
import com.github.dockerjava.api.model.Volume;
|
||||
import com.github.dockerjava.core.DefaultDockerClientConfig;
|
||||
import com.github.dockerjava.core.DockerClientBuilder;
|
||||
import com.github.dockerjava.core.DockerClientConfig;
|
||||
import com.github.dockerjava.core.command.ExecStartResultCallback;
|
||||
|
||||
import io.openvidu.client.OpenViduException;
|
||||
import io.openvidu.client.OpenViduException.Code;
|
||||
|
@ -57,6 +47,7 @@ import io.openvidu.server.kurento.core.KurentoSession;
|
|||
import io.openvidu.server.recording.CompositeWrapper;
|
||||
import io.openvidu.server.recording.Recording;
|
||||
import io.openvidu.server.recording.RecordingInfoUtils;
|
||||
import io.openvidu.server.utils.DockerManager;
|
||||
|
||||
public class ComposedRecordingService extends RecordingService {
|
||||
|
||||
|
@ -66,12 +57,11 @@ public class ComposedRecordingService extends RecordingService {
|
|||
private Map<String, String> sessionsContainers = new ConcurrentHashMap<>();
|
||||
private Map<String, CompositeWrapper> composites = new ConcurrentHashMap<>();
|
||||
|
||||
DockerClient dockerClient;
|
||||
private DockerManager dockerManager;
|
||||
|
||||
public ComposedRecordingService(RecordingManager recordingManager, OpenviduConfig openviduConfig) {
|
||||
super(recordingManager, openviduConfig);
|
||||
DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().build();
|
||||
this.dockerClient = DockerClientBuilder.getInstance(config).build();
|
||||
this.dockerManager = new DockerManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -160,7 +150,20 @@ public class ComposedRecordingService extends RecordingService {
|
|||
|
||||
String containerId;
|
||||
try {
|
||||
containerId = this.runRecordingContainer(envs, "recording_" + recording.getId());
|
||||
final String container = RecordingManager.IMAGE_NAME + ":" + RecordingManager.IMAGE_TAG;
|
||||
final String containerName = "recording_" + recording.getId();
|
||||
Volume volume1 = new Volume("/recordings");
|
||||
Volume volume2 = new Volume("/dev/shm");
|
||||
List<Volume> volumes = new ArrayList<>();
|
||||
volumes.add(volume1);
|
||||
volumes.add(volume2);
|
||||
Bind bind1 = new Bind(openviduConfig.getOpenViduRecordingPath(), volume1);
|
||||
Bind bind2 = new Bind("/dev/shm", volume2);
|
||||
List<Bind> binds = new ArrayList<>();
|
||||
binds.add(bind1);
|
||||
binds.add(bind2);
|
||||
containerId = dockerManager.runContainer(container, containerName, volumes, binds, envs);
|
||||
containers.put(containerId, containerName);
|
||||
} catch (Exception e) {
|
||||
this.cleanRecordingMaps(recording);
|
||||
throw this.failStartRecording(session, recording,
|
||||
|
@ -251,8 +254,9 @@ public class ComposedRecordingService extends RecordingService {
|
|||
} else {
|
||||
log.warn("Removing container {} for closed session {}...", containerIdAux,
|
||||
session.getSessionId());
|
||||
dockerClient.stopContainerCmd(containerIdAux).exec();
|
||||
this.removeDockerContainer(containerIdAux);
|
||||
dockerManager.stopDockerContainer(containerIdAux);
|
||||
dockerManager.removeDockerContainer(containerIdAux, false);
|
||||
containers.remove(containerId);
|
||||
containerClosed = true;
|
||||
log.warn("Container {} for closed session {} succesfully stopped and removed", containerIdAux,
|
||||
session.getSessionId());
|
||||
|
@ -272,36 +276,25 @@ public class ComposedRecordingService extends RecordingService {
|
|||
} else {
|
||||
|
||||
// Gracefully stop ffmpeg process
|
||||
ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId).withAttachStdout(true)
|
||||
.withAttachStderr(true).withCmd("bash", "-c", "echo 'q' > stop").exec();
|
||||
try {
|
||||
dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec(new ExecStartResultCallback())
|
||||
.awaitCompletion();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
dockerManager.runCommandInContainer(containerId, "echo 'q' > stop");
|
||||
} catch (InterruptedException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
|
||||
// Wait for the container to be gracefully self-stopped
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
WaitForContainerStoppedCallback callback = new WaitForContainerStoppedCallback(latch);
|
||||
dockerClient.waitContainerCmd(containerId).exec(callback);
|
||||
|
||||
boolean stopped = false;
|
||||
final int timeOfWait = 30;
|
||||
try {
|
||||
stopped = latch.await(30, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
dockerManager.waitForContainerStopped(containerId, timeOfWait);
|
||||
} catch (Exception e) {
|
||||
failRecordingCompletion(recording, containerId,
|
||||
new OpenViduException(Code.RECORDING_COMPLETION_ERROR_CODE,
|
||||
"The recording completion process has been unexpectedly interrupted"));
|
||||
}
|
||||
if (!stopped) {
|
||||
failRecordingCompletion(recording, containerId,
|
||||
new OpenViduException(Code.RECORDING_COMPLETION_ERROR_CODE,
|
||||
"The recording completion process couldn't finish in 30 seconds"));
|
||||
"The recording completion process couldn't finish in " + timeOfWait + " seconds"));
|
||||
}
|
||||
|
||||
// Remove container
|
||||
this.removeDockerContainer(containerId);
|
||||
dockerManager.removeDockerContainer(containerId, false);
|
||||
containers.remove(containerId);
|
||||
|
||||
// Update recording attributes reading from video report file
|
||||
try {
|
||||
|
@ -390,41 +383,6 @@ public class ComposedRecordingService extends RecordingService {
|
|||
return recording;
|
||||
}
|
||||
|
||||
private String runRecordingContainer(List<String> envs, String containerName) throws Exception {
|
||||
Volume volume1 = new Volume("/recordings");
|
||||
Volume volume2 = new Volume("/dev/shm");
|
||||
CreateContainerCmd cmd = dockerClient
|
||||
.createContainerCmd(RecordingManager.IMAGE_NAME + ":" + RecordingManager.IMAGE_TAG)
|
||||
.withName(containerName).withEnv(envs).withNetworkMode("host").withVolumes(volume1)
|
||||
.withBinds(new Bind(openviduConfig.getOpenViduRecordingPath(), volume1), new Bind("/dev/shm", volume2));
|
||||
CreateContainerResponse container = null;
|
||||
try {
|
||||
container = cmd.exec();
|
||||
dockerClient.startContainerCmd(container.getId()).exec();
|
||||
containers.put(container.getId(), containerName);
|
||||
log.info("Container ID: {}", container.getId());
|
||||
return container.getId();
|
||||
} catch (ConflictException e) {
|
||||
log.error(
|
||||
"The container name {} is already in use. Probably caused by a session with unique publisher re-publishing a stream",
|
||||
containerName);
|
||||
throw e;
|
||||
} catch (NotFoundException e) {
|
||||
log.error("Docker image {} couldn't be found in docker host",
|
||||
RecordingManager.IMAGE_NAME + ":" + RecordingManager.IMAGE_TAG);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void removeDockerContainer(String containerId) {
|
||||
dockerClient.removeContainerCmd(containerId).exec();
|
||||
containers.remove(containerId);
|
||||
}
|
||||
|
||||
private void stopDockerContainer(String containerId) {
|
||||
dockerClient.stopContainerCmd(containerId).exec();
|
||||
}
|
||||
|
||||
private void waitForVideoFileNotEmpty(Recording recording) throws OpenViduException {
|
||||
boolean isPresent = false;
|
||||
int i = 1;
|
||||
|
@ -451,8 +409,9 @@ public class ComposedRecordingService extends RecordingService {
|
|||
private void failRecordingCompletion(Recording recording, String containerId, OpenViduException e)
|
||||
throws OpenViduException {
|
||||
recording.setStatus(io.openvidu.java.client.Recording.Status.failed);
|
||||
this.stopDockerContainer(containerId);
|
||||
this.removeDockerContainer(containerId);
|
||||
dockerManager.stopDockerContainer(containerId);
|
||||
dockerManager.removeDockerContainer(containerId, true);
|
||||
containers.remove(containerId);
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,8 +37,6 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.ws.rs.ProcessingException;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.kurento.client.ErrorEvent;
|
||||
import org.kurento.client.EventListener;
|
||||
|
@ -51,11 +49,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.github.dockerjava.api.exception.DockerClientException;
|
||||
import com.github.dockerjava.api.exception.InternalServerErrorException;
|
||||
import com.github.dockerjava.api.exception.NotFoundException;
|
||||
import com.github.dockerjava.api.model.Container;
|
||||
import com.github.dockerjava.core.command.PullImageResultCallback;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
|
@ -74,6 +67,7 @@ import io.openvidu.server.kurento.KurentoClientSessionInfo;
|
|||
import io.openvidu.server.kurento.OpenViduKurentoClientSessionInfo;
|
||||
import io.openvidu.server.recording.Recording;
|
||||
import io.openvidu.server.utils.CustomFileManager;
|
||||
import io.openvidu.server.utils.DockerManager;
|
||||
|
||||
@Service
|
||||
public class RecordingManager {
|
||||
|
@ -83,6 +77,7 @@ public class RecordingManager {
|
|||
RecordingService recordingService;
|
||||
private ComposedRecordingService composedRecordingService;
|
||||
private SingleStreamRecordingService singleStreamRecordingService;
|
||||
private DockerManager dockerManager;
|
||||
|
||||
@Autowired
|
||||
protected SessionEventsHandler sessionHandler;
|
||||
|
@ -105,7 +100,7 @@ public class RecordingManager {
|
|||
Runtime.getRuntime().availableProcessors());
|
||||
|
||||
static final String RECORDING_ENTITY_FILE = ".recording.";
|
||||
static final String IMAGE_NAME = "openvidu/openvidu-recording";
|
||||
public static final String IMAGE_NAME = "openvidu/openvidu-recording";
|
||||
static String IMAGE_TAG;
|
||||
|
||||
private static final List<EndReason> LAST_PARTICIPANT_LEFT_REASONS = Arrays
|
||||
|
@ -124,6 +119,7 @@ public class RecordingManager {
|
|||
|
||||
RecordingManager.IMAGE_TAG = openviduConfig.getOpenViduRecordingVersion();
|
||||
|
||||
this.dockerManager = new DockerManager();
|
||||
this.composedRecordingService = new ComposedRecordingService(this, openviduConfig);
|
||||
this.singleStreamRecordingService = new SingleStreamRecordingService(this, openviduConfig);
|
||||
|
||||
|
@ -133,7 +129,7 @@ public class RecordingManager {
|
|||
this.checkRecordingRequirements(this.openviduConfig.getOpenViduRecordingPath(),
|
||||
this.openviduConfig.getOpenviduRecordingCustomLayout());
|
||||
|
||||
if (this.recordingImageExistsLocally()) {
|
||||
if (dockerManager.dockerImageExistsLocally(IMAGE_NAME + ":" + IMAGE_TAG)) {
|
||||
log.info("Docker image already exists locally");
|
||||
} else {
|
||||
Thread t = new Thread(() -> {
|
||||
|
@ -150,7 +146,7 @@ public class RecordingManager {
|
|||
}
|
||||
});
|
||||
t.start();
|
||||
this.downloadRecordingImage();
|
||||
dockerManager.downloadDockerImage(IMAGE_NAME + ":" + IMAGE_TAG);
|
||||
t.interrupt();
|
||||
try {
|
||||
t.join();
|
||||
|
@ -161,12 +157,15 @@ public class RecordingManager {
|
|||
}
|
||||
|
||||
// Clean any stranded openvidu/openvidu-recording container on startup
|
||||
this.removeExistingRecordingContainers();
|
||||
dockerManager.cleanStrandedContainers(RecordingManager.IMAGE_NAME);
|
||||
}
|
||||
|
||||
public void checkRecordingRequirements(String openviduRecordingPath, String openviduRecordingCustomLayout)
|
||||
throws OpenViduException {
|
||||
this.checkDockerEnabled();
|
||||
if (dockerManager == null) {
|
||||
this.dockerManager = new DockerManager();
|
||||
}
|
||||
dockerManager.checkDockerEnabled(openviduConfig.getSpringProfile());
|
||||
this.checkRecordingPaths(openviduRecordingPath, openviduRecordingCustomLayout);
|
||||
}
|
||||
|
||||
|
@ -441,53 +440,6 @@ public class RecordingManager {
|
|||
return recording;
|
||||
}
|
||||
|
||||
private void removeExistingRecordingContainers() {
|
||||
List<Container> existingContainers = this.composedRecordingService.dockerClient.listContainersCmd()
|
||||
.withShowAll(true).exec();
|
||||
for (Container container : existingContainers) {
|
||||
if (container.getImage().startsWith(RecordingManager.IMAGE_NAME)) {
|
||||
log.info("Stranded openvidu/openvidu-recording Docker container ({}) removed on startup",
|
||||
container.getId());
|
||||
this.composedRecordingService.dockerClient.removeContainerCmd(container.getId()).withForce(true).exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean recordingImageExistsLocally() {
|
||||
boolean imageExists = false;
|
||||
try {
|
||||
this.composedRecordingService.dockerClient.inspectImageCmd(IMAGE_NAME + ":" + IMAGE_TAG).exec();
|
||||
imageExists = true;
|
||||
} catch (NotFoundException nfe) {
|
||||
imageExists = false;
|
||||
} catch (ProcessingException e) {
|
||||
throw e;
|
||||
} catch (NullPointerException e) {
|
||||
// Restarting openvidu-server from openvidu.recording=false
|
||||
// ComposedRecordingService was not initialized
|
||||
this.composedRecordingService = new ComposedRecordingService(this, openviduConfig);
|
||||
return this.recordingImageExistsLocally();
|
||||
}
|
||||
return imageExists;
|
||||
}
|
||||
|
||||
private void downloadRecordingImage() {
|
||||
try {
|
||||
this.composedRecordingService.dockerClient.pullImageCmd(IMAGE_NAME + ":" + IMAGE_TAG)
|
||||
.exec(new PullImageResultCallback()).awaitSuccess();
|
||||
} catch (NotFoundException | InternalServerErrorException e) {
|
||||
if (recordingImageExistsLocally()) {
|
||||
log.info("Docker image '{}' exists locally", IMAGE_NAME + ":" + IMAGE_TAG);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
} catch (DockerClientException e) {
|
||||
log.info("Error on Pulling '{}' image. Probably because the user has stopped the execution",
|
||||
IMAGE_NAME + ":" + IMAGE_TAG);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private Recording getRecordingFromHost(String recordingId) {
|
||||
log.info(this.openviduConfig.getOpenViduRecordingPath() + recordingId + "/"
|
||||
+ RecordingManager.RECORDING_ENTITY_FILE + recordingId);
|
||||
|
@ -542,29 +494,6 @@ public class RecordingManager {
|
|||
return fileNamesNoExtension;
|
||||
}
|
||||
|
||||
private void checkDockerEnabled() throws OpenViduException {
|
||||
try {
|
||||
this.recordingImageExistsLocally();
|
||||
} catch (ProcessingException exception) {
|
||||
String message = "Exception connecting to Docker daemon: ";
|
||||
if ("docker".equals(openviduConfig.getSpringProfile())) {
|
||||
final String NEW_LINE = System.getProperty("line.separator");
|
||||
message += "make sure you include the following flags in your \"docker run\" command:" + NEW_LINE
|
||||
+ " -e openvidu.recording.path=/YOUR/PATH/TO/VIDEO/FILES" + NEW_LINE
|
||||
+ " -e MY_UID=$(id -u $USER)" + NEW_LINE + " -v /var/run/docker.sock:/var/run/docker.sock"
|
||||
+ NEW_LINE + " -v /YOUR/PATH/TO/VIDEO/FILES:/YOUR/PATH/TO/VIDEO/FILES" + NEW_LINE;
|
||||
} else {
|
||||
message += "you need Docker CE installed in this machine to enable OpenVidu recording service. "
|
||||
+ "If Docker CE is already installed, make sure to add OpenVidu Server user to "
|
||||
+ "\"docker\" group: " + System.lineSeparator() + " 1) $ sudo usermod -aG docker $USER"
|
||||
+ System.lineSeparator()
|
||||
+ " 2) Log out and log back to the host to reevaluate group membership";
|
||||
}
|
||||
log.error(message);
|
||||
throw new OpenViduException(Code.RECORDING_ENABLED_BUT_DOCKER_NOT_FOUND, message);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkRecordingPaths(String openviduRecordingPath, String openviduRecordingCustomLayout)
|
||||
throws OpenViduException {
|
||||
log.info("Initializing recording paths");
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2019 OpenVidu (https://openvidu.io/)
|
||||
*
|
||||
* 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 io.openvidu.server.utils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.ws.rs.ProcessingException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.github.dockerjava.api.DockerClient;
|
||||
import com.github.dockerjava.api.command.CreateContainerCmd;
|
||||
import com.github.dockerjava.api.command.CreateContainerResponse;
|
||||
import com.github.dockerjava.api.command.ExecCreateCmdResponse;
|
||||
import com.github.dockerjava.api.exception.ConflictException;
|
||||
import com.github.dockerjava.api.exception.DockerClientException;
|
||||
import com.github.dockerjava.api.exception.InternalServerErrorException;
|
||||
import com.github.dockerjava.api.exception.NotFoundException;
|
||||
import com.github.dockerjava.api.model.Bind;
|
||||
import com.github.dockerjava.api.model.Container;
|
||||
import com.github.dockerjava.api.model.Volume;
|
||||
import com.github.dockerjava.core.DefaultDockerClientConfig;
|
||||
import com.github.dockerjava.core.DockerClientBuilder;
|
||||
import com.github.dockerjava.core.DockerClientConfig;
|
||||
import com.github.dockerjava.core.command.ExecStartResultCallback;
|
||||
import com.github.dockerjava.core.command.PullImageResultCallback;
|
||||
|
||||
import io.openvidu.client.OpenViduException;
|
||||
import io.openvidu.client.OpenViduException.Code;
|
||||
import io.openvidu.server.recording.service.WaitForContainerStoppedCallback;
|
||||
|
||||
public class DockerManager {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(DockerManager.class);
|
||||
|
||||
DockerClient dockerClient;
|
||||
|
||||
public DockerManager() {
|
||||
DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().build();
|
||||
this.dockerClient = DockerClientBuilder.getInstance(config).build();
|
||||
}
|
||||
|
||||
public void downloadDockerImage(String image) {
|
||||
try {
|
||||
this.dockerClient.pullImageCmd(image).exec(new PullImageResultCallback()).awaitSuccess();
|
||||
} catch (NotFoundException | InternalServerErrorException e) {
|
||||
if (dockerImageExistsLocally(image)) {
|
||||
log.info("Docker image '{}' exists locally", image);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
} catch (DockerClientException e) {
|
||||
log.info("Error on Pulling '{}' image. Probably because the user has stopped the execution", image);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean dockerImageExistsLocally(String image) throws ProcessingException {
|
||||
boolean imageExists = false;
|
||||
try {
|
||||
this.dockerClient.inspectImageCmd(image).exec();
|
||||
imageExists = true;
|
||||
} catch (NotFoundException nfe) {
|
||||
imageExists = false;
|
||||
} catch (ProcessingException e) {
|
||||
throw e;
|
||||
}
|
||||
return imageExists;
|
||||
}
|
||||
|
||||
public void checkDockerEnabled(String springProfile) throws OpenViduException {
|
||||
try {
|
||||
this.dockerImageExistsLocally("hello-world");
|
||||
log.info("Docker is installed and enabled");
|
||||
} catch (ProcessingException exception) {
|
||||
String message = "Exception connecting to Docker daemon: ";
|
||||
if ("docker".equals(springProfile)) {
|
||||
final String NEW_LINE = System.getProperty("line.separator");
|
||||
message += "make sure you include the following flags in your \"docker run\" command:" + NEW_LINE
|
||||
+ " -e openvidu.recording.path=/YOUR/PATH/TO/VIDEO/FILES" + NEW_LINE
|
||||
+ " -e MY_UID=$(id -u $USER)" + NEW_LINE + " -v /var/run/docker.sock:/var/run/docker.sock"
|
||||
+ NEW_LINE + " -v /YOUR/PATH/TO/VIDEO/FILES:/YOUR/PATH/TO/VIDEO/FILES" + NEW_LINE;
|
||||
} else {
|
||||
message += "you need Docker CE installed in this machine to enable OpenVidu recording service. "
|
||||
+ "If Docker CE is already installed, make sure to add OpenVidu Server user to "
|
||||
+ "\"docker\" group: " + System.lineSeparator() + " 1) $ sudo usermod -aG docker $USER"
|
||||
+ System.lineSeparator()
|
||||
+ " 2) Log out and log back to the host to reevaluate group membership";
|
||||
}
|
||||
log.error(message);
|
||||
throw new OpenViduException(Code.DOCKER_NOT_FOUND, message);
|
||||
}
|
||||
}
|
||||
|
||||
public String runContainer(String container, String containerName, List<Volume> volumes, List<Bind> binds,
|
||||
List<String> envs) throws Exception {
|
||||
CreateContainerCmd cmd = dockerClient.createContainerCmd(container).withName(containerName).withEnv(envs)
|
||||
.withNetworkMode("host").withVolumes(volumes).withBinds(binds);
|
||||
CreateContainerResponse response = null;
|
||||
try {
|
||||
response = cmd.exec();
|
||||
dockerClient.startContainerCmd(response.getId()).exec();
|
||||
log.info("Container ID: {}", response.getId());
|
||||
return response.getId();
|
||||
} catch (ConflictException e) {
|
||||
log.error(
|
||||
"The container name {} is already in use. Probably caused by a session with unique publisher re-publishing a stream",
|
||||
containerName);
|
||||
throw e;
|
||||
} catch (NotFoundException e) {
|
||||
log.error("Docker image {} couldn't be found in docker host", container);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void removeDockerContainer(String containerId, boolean force) {
|
||||
dockerClient.removeContainerCmd(containerId).withForce(force).exec();
|
||||
}
|
||||
|
||||
public void stopDockerContainer(String containerId) {
|
||||
dockerClient.stopContainerCmd(containerId).exec();
|
||||
}
|
||||
|
||||
public void cleanStrandedContainers(String imageName) {
|
||||
List<Container> existingContainers = this.dockerClient.listContainersCmd().withShowAll(true).exec();
|
||||
for (Container container : existingContainers) {
|
||||
if (container.getImage().startsWith(imageName)) {
|
||||
log.info("Stranded {} Docker container ({}) removed on startup", imageName, container.getId());
|
||||
this.dockerClient.removeContainerCmd(container.getId()).withForce(true).exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void runCommandInContainer(String containerId, String command) throws InterruptedException {
|
||||
ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId).withAttachStdout(true)
|
||||
.withAttachStderr(true).withCmd("bash", "-c", command).exec();
|
||||
dockerClient.execStartCmd(execCreateCmdResponse.getId()).exec(new ExecStartResultCallback()).awaitCompletion();
|
||||
}
|
||||
|
||||
public void waitForContainerStopped(String containerId, int secondsOfWait) throws Exception {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
WaitForContainerStoppedCallback callback = new WaitForContainerStoppedCallback(latch);
|
||||
dockerClient.waitContainerCmd(containerId).exec(callback);
|
||||
boolean stopped = false;
|
||||
try {
|
||||
stopped = latch.await(secondsOfWait, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
throw e;
|
||||
}
|
||||
if (!stopped) {
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue