openvidu-test-e2e: mediaServerReconnect tests

This commit is contained in:
pabloFuente 2021-10-30 19:28:30 +02:00
parent 82115a7ce8
commit 9b599ebb6a
5 changed files with 466 additions and 193 deletions

View File

@ -21,18 +21,18 @@ public enum EndReason {
/**
* A user called the RPC operation to unsubscribe from a remote stream. Applies
* to webrtcConnectionDestroyed
* to event webrtcConnectionDestroyed
*/
unsubscribe,
/**
* A user called the RPC operation to unpublish a local stream. Applies to
* A user called the RPC operation to unpublish a local stream. Applies to event
* webrtcConnectionDestroyed
*/
unpublish,
/**
* A user called the RPC operation to leave the session. Applies to
* A user called the RPC operation to leave the session. Applies to events
* webrtcConnectionDestroyed and participantLeft. Can trigger other events with
* lastParticipantLeft
*/
@ -40,49 +40,49 @@ public enum EndReason {
/**
* A user called the RPC operation to force the unpublishing of a remote stream.
* Applies to webrtcConnectionDestroyed
* Applies to event webrtcConnectionDestroyed
*/
forceUnpublishByUser,
/**
* The server application called the REST operation to force the unpublishing of
* a user's stream. Applies to webrtcConnectionDestroyed
* a user's stream. Applies to event webrtcConnectionDestroyed
*/
forceUnpublishByServer,
/**
* A user called the RPC operation to force the disconnection of a remote user.
* Applies to webrtcConnectionDestroyed and participantLeft. Can trigger other
* events with lastParticipantLeft
* Applies to events webrtcConnectionDestroyed and participantLeft. Can trigger
* other events with lastParticipantLeft
*/
forceDisconnectByUser,
/**
* The server application called the REST operation to force the disconnection
* of a user. Applies to webrtcConnectionDestroyed and participantLeft. Can
* trigger other events with lastParticipantLeft
* of a user. Applies to events webrtcConnectionDestroyed and participantLeft.
* Can trigger other events with lastParticipantLeft
*/
forceDisconnectByServer,
/**
* The last participant left the session, which caused the session to be closed.
* Applies to webrtcConnectionDestroyed, participantLeft, recordingStatusChanged
* and sessionDestroyed. Can be triggered from other events with other end
* reasons (disconnect, forceDisconnectByUser, forceDisconnectByServer,
* networkDisconnect)
* Applies to events webrtcConnectionDestroyed, participantLeft,
* recordingStatusChanged and sessionDestroyed. Can be triggered from other
* events with other end reasons (disconnect, forceDisconnectByUser,
* forceDisconnectByServer, networkDisconnect)
*/
lastParticipantLeft,
/**
* The server application called the REST operation to stop a recording. Applies
* to recordingStatusChanged
* to event recordingStatusChanged
*/
recordingStoppedByServer,
/**
* The server application called the REST operation to close a session. Applies
* to webrtcConnectionDestroyed, participantLeft, recordingStatusChanged and
* sessionDestroyed
* to events webrtcConnectionDestroyed, participantLeft, recordingStatusChanged
* and sessionDestroyed
*/
sessionClosedByServer,
@ -95,7 +95,7 @@ public enum EndReason {
/**
* A media server disconnected. This is reserved for Media Nodes being
* gracefully removed from an OpenVidu Pro cluster. Applies to
* gracefully removed from an OpenVidu Pro cluster. Applies to events
* webrtcConnectionDestroyed, participantLeft, recordingStatusChanged and
* sessionDestroyed
*/
@ -103,29 +103,29 @@ public enum EndReason {
/**
* A media server disconnected, and a new one automatically reconnected. All of
* the media endpoints were destroyed in the process. Applies to
* the media endpoints were destroyed in the process. Applies to events
* webrtcConnectionDestroyed and recordingStatusChanged
*/
mediaServerReconnect,
/**
* A node has crashed. For now this means a Media Node has crashed. Applies to
* webrtcConnectionDestroyed, participantLeft, recordingStatusChanged and
* events webrtcConnectionDestroyed, participantLeft, recordingStatusChanged and
* sessionDestroyed
*/
nodeCrashed,
/**
* OpenVidu Server has gracefully stopped. This is reserved for OpenVidu Pro
* restart operation. Applies to webrtcConnectionDestroyed, participantLeft,
* recordingStatusChanged and sessionDestroyed
* restart operation. Applies to events webrtcConnectionDestroyed,
* participantLeft, recordingStatusChanged and sessionDestroyed
*/
openviduServerStopped,
/**
* A recording has been stopped automatically
* (https://docs.openvidu.io/en/stable/advanced-features/recording/#automatic-stop-of-recordings).
* Applies to recordingStatusChanged
* Applies to event recordingStatusChanged
*/
automaticStop

View File

@ -246,6 +246,15 @@ public class KurentoSession extends Session {
}
}
private void resetPipeline(Runnable callback) {
pipeline = null;
pipelineLatch = new CountDownLatch(1);
pipelineCreationErrorCause = null;
if (callback != null) {
callback.run();
}
}
private void closePipeline(Runnable callback) {
synchronized (pipelineReleaseLock) {
if (pipeline == null) {
@ -260,25 +269,17 @@ public class KurentoSession extends Session {
@Override
public void onSuccess(Void result) throws Exception {
log.debug("SESSION {}: Released Pipeline", sessionId);
pipeline = null;
pipelineLatch = new CountDownLatch(1);
pipelineCreationErrorCause = null;
if (callback != null) {
callback.run();
}
resetPipeline(callback);
}
@Override
public void onError(Throwable cause) throws Exception {
log.warn("SESSION {}: Could not successfully release Pipeline", sessionId, cause);
pipeline = null;
pipelineLatch = new CountDownLatch(1);
pipelineCreationErrorCause = null;
if (callback != null) {
callback.run();
}
resetPipeline(callback);
}
});
} else {
resetPipeline(callback);
}
}
}
@ -330,27 +331,33 @@ public class KurentoSession extends Session {
// Release pipeline, create a new one and prepare new PublisherEndpoints for
// allowed users
log.info("Resetting process: closing media pipeline for active session {}", this.sessionId);
this.closePipeline(() -> {
log.info("Resetting process: media pipeline closed for active session {}", this.sessionId);
createPipeline();
try {
if (!pipelineLatch.await(20, TimeUnit.SECONDS)) {
throw new Exception("MediaPipeline was not created in 20 seconds");
}
getParticipants().forEach(p -> {
if (!OpenViduRole.SUBSCRIBER.equals(p.getToken().getRole())) {
((KurentoParticipant) p).resetPublisherEndpoint(mediaOptionsMap.get(p.getParticipantPublicId()),
null);
try {
RemoteOperationUtils.setToSkipRemoteOperations();
this.closePipeline(() -> {
RemoteOperationUtils.revertToRunRemoteOperations();
log.info("Resetting process: media pipeline closed for active session {}", this.sessionId);
createPipeline();
try {
if (!pipelineLatch.await(20, TimeUnit.SECONDS)) {
throw new Exception("MediaPipeline was not created in 20 seconds");
}
});
log.info(
"Resetting process: media pipeline created and publisher endpoints reseted for active session {}",
this.sessionId);
} catch (Exception e) {
log.error("Error waiting to new MediaPipeline on KurentoSession restart: {}", e.getMessage());
}
});
getParticipants().forEach(p -> {
if (!OpenViduRole.SUBSCRIBER.equals(p.getToken().getRole())) {
((KurentoParticipant) p)
.resetPublisherEndpoint(mediaOptionsMap.get(p.getParticipantPublicId()), null);
}
});
log.info(
"Resetting process: media pipeline created and publisher endpoints reseted for active session {}",
this.sessionId);
} catch (Exception e) {
log.error("Error waiting to new MediaPipeline on KurentoSession restart: {}", e.getMessage());
}
});
} finally {
RemoteOperationUtils.revertToRunRemoteOperations();
}
}
@Override

View File

@ -362,7 +362,6 @@ public class RecordingManager {
}
((RecordingService) singleStreamRecordingService).sealRecordingMetadataFileAsStopped(recording);
final long timestamp = System.currentTimeMillis();
this.cdr.recordRecordingStatusChanged(recording, reason, timestamp, Status.stopped);
@ -384,6 +383,11 @@ public class RecordingManager {
public Recording forceStopRecording(Session session, EndReason reason, Long kmsDisconnectionTime) {
Recording recording;
recording = this.sessionsRecordings.get(session.getSessionId());
((RecordingService) singleStreamRecordingService).sealRecordingMetadataFileAsStopped(recording);
final long timestamp = System.currentTimeMillis();
this.cdr.recordRecordingStatusChanged(recording, reason, timestamp, Status.stopped);
switch (recording.getOutputMode()) {
case COMPOSED:
recording = this.composedRecordingService.stopRecording(session, recording, reason, kmsDisconnectionTime);

View File

@ -253,12 +253,26 @@ public class AbstractOpenViduTestAppE2eTest {
other.dispose();
it.remove();
}
this.closeAllSessions(OV);
if (isRecordingTest) {
deleteAllRecordings(OV);
isRecordingTest = false;
}
if (isKurentoRestartTest) {
this.stopMediaServer(false);
this.startMediaServer(true);
isKurentoRestartTest = false;
}
OV = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET);
}
protected void closeAllSessions(OpenVidu client) {
try {
OV.fetch();
client.fetch();
} catch (OpenViduJavaClientException | OpenViduHttpException e1) {
log.error("Error fetching sessions: {}", e1.getMessage());
}
OV.getActiveSessions().forEach(session -> {
client.getActiveSessions().forEach(session -> {
try {
session.close();
log.info("Session {} successfully closed", session.getSessionId());
@ -268,20 +282,29 @@ public class AbstractOpenViduTestAppE2eTest {
log.error("Error closing session: {}", e.getMessage());
}
});
if (isRecordingTest) {
removeAllRecordingContiners();
try {
FileUtils.cleanDirectory(new File("/opt/openvidu/recordings"));
} catch (IOException e) {
log.error(e.getMessage());
}
isRecordingTest = false;
}
protected void deleteAllRecordings(OpenVidu client) {
try {
client.listRecordings().forEach(recording -> {
try {
client.deleteRecording(recording.getId());
log.info("Recording {} successfully deleted", recording.getId());
} catch (OpenViduJavaClientException e) {
log.error("Error deleting recording: {}", e.getMessage());
} catch (OpenViduHttpException e) {
log.error("Error deleting recording: {}", e.getMessage());
}
});
} catch (OpenViduJavaClientException | OpenViduHttpException e) {
log.error("Error listing recordings: {}", e.getMessage());
}
if (isKurentoRestartTest) {
this.restartMediaServer();
isKurentoRestartTest = false;
removeAllRecordingContiners();
try {
FileUtils.cleanDirectory(new File("/opt/openvidu/recordings"));
} catch (IOException e) {
log.error(e.getMessage());
}
OV = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET);
}
protected void listEmptyRecordings() {
@ -318,7 +341,7 @@ public class AbstractOpenViduTestAppE2eTest {
return "data:image/png;base64," + screenshotBase64;
}
protected void startMediaServer() {
protected void startMediaServer(boolean waitUntilKurentoClientReconnection) {
String command = null;
if (MEDIA_SERVER_IMAGE.startsWith(KURENTO_IMAGE)) {
log.info("Starting kurento");
@ -336,9 +359,16 @@ public class AbstractOpenViduTestAppE2eTest {
System.exit(1);
}
commandLine.executeCommand(command);
if (waitUntilKurentoClientReconnection) {
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
protected void stopMediaServer() {
protected void stopMediaServer(boolean waitUntilNodeCrashedEvent) {
final String dockerRemoveCmd = "docker ps -a | awk '{ print $1,$2 }' | grep GREP_PARAMETER | awk '{ print $1 }' | xargs -I {} docker rm -f {}";
String grep = null;
if (MEDIA_SERVER_IMAGE.startsWith(KURENTO_IMAGE)) {
@ -352,17 +382,13 @@ public class AbstractOpenViduTestAppE2eTest {
System.exit(1);
}
commandLine.executeCommand(dockerRemoveCmd.replaceFirst("GREP_PARAMETER", grep));
}
protected void restartMediaServer() {
this.stopMediaServer();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.err.println("Error restarting media server");
e.printStackTrace();
if (waitUntilNodeCrashedEvent) {
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.startMediaServer();
}
protected void checkDockerContainerRunning(String imageName, int amount) {