mirror of https://github.com/OpenVidu/openvidu.git
openvidu-test-e2e: do not use a shared cache between test runs for server SDK apps
parent
cf1512c108
commit
7b8b072005
|
|
@ -16,6 +16,7 @@ import java.nio.file.Paths;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.security.KeyManagementException;
|
import java.security.KeyManagementException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
@ -43,6 +44,9 @@ import org.testcontainers.containers.wait.strategy.Wait;
|
||||||
import org.testcontainers.containers.wait.strategy.WaitStrategy;
|
import org.testcontainers.containers.wait.strategy.WaitStrategy;
|
||||||
import org.testcontainers.utility.DockerImageName;
|
import org.testcontainers.utility.DockerImageName;
|
||||||
|
|
||||||
|
import com.github.dockerjava.api.model.Mount;
|
||||||
|
import com.github.dockerjava.api.model.MountType;
|
||||||
|
|
||||||
import io.livekit.server.AccessToken;
|
import io.livekit.server.AccessToken;
|
||||||
import io.livekit.server.CanPublish;
|
import io.livekit.server.CanPublish;
|
||||||
import io.livekit.server.IngressServiceClient;
|
import io.livekit.server.IngressServiceClient;
|
||||||
|
|
@ -470,6 +474,34 @@ public class OpenViduTestE2e {
|
||||||
startServerSdkPublisher(sdk, roomName, codec, 1);
|
startServerSdkPublisher(sdk, roomName, codec, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dependency cache volumes of the server SDK publishers, one per SDK, created
|
||||||
|
// on first use and alive only for this JVM.
|
||||||
|
private static final Map<String, String> SDK_CACHE_VOLUMES = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the dependency cache volume of the given SDK for this JVM, creating
|
||||||
|
* it on first use. The volume carries the Testcontainers session labels, so
|
||||||
|
* Ryuk removes it when the JVM ends, the same way it removes the containers;
|
||||||
|
* a shutdown hook removes it too for the runs that disable Ryuk.
|
||||||
|
*/
|
||||||
|
private static String sdkCacheVolume(String sdk) {
|
||||||
|
return SDK_CACHE_VOLUMES.computeIfAbsent(sdk, s -> {
|
||||||
|
String name = "openvidu-e2e-sdk-cache-" + s + "-" + DockerClientFactory.SESSION_ID;
|
||||||
|
Map<String, String> labels = new HashMap<>(DockerClientFactory.DEFAULT_LABELS);
|
||||||
|
labels.put(DockerClientFactory.TESTCONTAINERS_SESSION_ID_LABEL, DockerClientFactory.SESSION_ID);
|
||||||
|
DockerClientFactory.instance().client().createVolumeCmd().withName(name).withLabels(labels).exec();
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||||
|
try {
|
||||||
|
DockerClientFactory.instance().client().removeVolumeCmd(name).exec();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Already removed by Ryuk, or still used by a container Ryuk is
|
||||||
|
// about to remove together with the volume
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return name;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a LiveKit server RTC SDK participant (a minimal program at
|
* Starts a LiveKit server RTC SDK participant (a minimal program at
|
||||||
* src/test/resources/<sdk>-publisher) in a plain runtime container
|
* src/test/resources/<sdk>-publisher) in a plain runtime container
|
||||||
|
|
@ -478,8 +510,11 @@ public class OpenViduTestE2e {
|
||||||
* simulcast for VP8/H264 and without SVC (scalabilityMode L1T1) for VP9/AV1.
|
* simulcast for VP8/H264 and without SVC (scalabilityMode L1T1) for VP9/AV1.
|
||||||
*
|
*
|
||||||
* The container mounts the program read-only at /app, copies it to /work and
|
* The container mounts the program read-only at /app, copies it to /work and
|
||||||
* runs it there, with a per-SDK dependency cache mounted at /cache so
|
* runs it there. /cache is a Docker volume that exists only for the current
|
||||||
* repeated runs skip downloads. Returns once the program logs
|
* test JVM (see {@link #sdkCacheVolume}): nothing of a previous run or of
|
||||||
|
* another job on the same machine reaches the program, while the publishers
|
||||||
|
* of one SDK still share their downloads (and the rust build, which takes up
|
||||||
|
* to 30 minutes from cold) within the run. Returns once the program logs
|
||||||
* TRACK_PUBLISHED. The container is stopped automatically on test dispose.
|
* TRACK_PUBLISHED. The container is stopped automatically on test dispose.
|
||||||
*/
|
*/
|
||||||
public void startServerSdkPublisher(String sdk, String roomName, String codec, int layers) throws Exception {
|
public void startServerSdkPublisher(String sdk, String roomName, String codec, int layers) throws Exception {
|
||||||
|
|
@ -542,14 +577,21 @@ public class OpenViduTestE2e {
|
||||||
env.put("VIDEO_LAYERS", String.valueOf(layers));
|
env.put("VIDEO_LAYERS", String.valueOf(layers));
|
||||||
|
|
||||||
String programDir = Paths.get("src/test/resources/" + sdk + "-publisher").toAbsolutePath().toString();
|
String programDir = Paths.get("src/test/resources/" + sdk + "-publisher").toAbsolutePath().toString();
|
||||||
Path cacheDir = Paths.get(System.getProperty("java.io.tmpdir"), "openvidu-e2e-sdk-cache", sdk);
|
String cacheVolume = sdkCacheVolume(sdk);
|
||||||
Files.createDirectories(cacheDir);
|
|
||||||
|
|
||||||
GenericContainer<?> publisherContainer = new GenericContainer<>(DockerImageName.parse(image))
|
GenericContainer<?> publisherContainer = new GenericContainer<>(DockerImageName.parse(image))
|
||||||
.withCreateContainerCmdModifier(
|
.withCreateContainerCmdModifier(
|
||||||
cmd -> cmd.withName(sdk + "-publisher-" + (int) (Math.random() * 100000)))
|
cmd -> cmd.withName(sdk + "-publisher-" + (int) (Math.random() * 100000)))
|
||||||
.withNetworkMode("host").withFileSystemBind(programDir, "/app", BindMode.READ_ONLY)
|
// Not withFileSystemBind: it resolves its first argument as a host path
|
||||||
.withFileSystemBind(cacheDir.toString(), "/cache").withEnv(env)
|
.withCreateContainerCmdModifier(cmd -> {
|
||||||
|
List<Mount> mounts = new ArrayList<>();
|
||||||
|
if (cmd.getHostConfig().getMounts() != null) {
|
||||||
|
mounts.addAll(cmd.getHostConfig().getMounts());
|
||||||
|
}
|
||||||
|
mounts.add(new Mount().withType(MountType.VOLUME).withSource(cacheVolume).withTarget("/cache"));
|
||||||
|
cmd.getHostConfig().withMounts(mounts);
|
||||||
|
})
|
||||||
|
.withNetworkMode("host").withFileSystemBind(programDir, "/app", BindMode.READ_ONLY).withEnv(env)
|
||||||
.withCommand("sh", "-c", "mkdir -p /work && cp -r /app/. /work && cd /work && " + runCommand)
|
.withCommand("sh", "-c", "mkdir -p /work && cp -r /app/. /work && cd /work && " + runCommand)
|
||||||
.withLogConsumer(
|
.withLogConsumer(
|
||||||
frame -> log.info("[{}-publisher] {}", sdk, frame.getUtf8String().stripTrailing()))
|
frame -> log.info("[{}-publisher] {}", sdk, frame.getUtf8String().stripTrailing()))
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,14 @@ Minimal LiveKit server RTC SDK publisher programs used by
|
||||||
| `dotnet-publisher/` | `Livekit.Rtc.Dotnet` | synthetic frames (FFI) |
|
| `dotnet-publisher/` | `Livekit.Rtc.Dotnet` | synthetic frames (FFI) |
|
||||||
|
|
||||||
Each program is launched by `OpenViduTestE2e#startServerSdkPublisher` inside a
|
Each program is launched by `OpenViduTestE2e#startServerSdkPublisher` inside a
|
||||||
plain runtime container of its language (see that method for the images, run
|
plain runtime container of its language (see that method for the images and run
|
||||||
commands and per-SDK dependency caches). The directory is mounted read-only at
|
commands). The directory is mounted read-only at `/app`, copied to `/work` and
|
||||||
`/app`, copied to `/work` and run there. The program must print the exact line
|
run there. The dependency cache at `/cache` is a Docker volume that lives only
|
||||||
|
for the test JVM that created it (one per SDK, removed with the containers when
|
||||||
|
the JVM ends), so a run never sees the downloads, builds or caches of a previous
|
||||||
|
run or of another job on the same machine; within a run the publishers of an SDK
|
||||||
|
share it, which keeps the Rust publisher's libwebrtc build to one per run. The
|
||||||
|
program must print the exact line
|
||||||
`TRACK_PUBLISHED` once the track is published: the Java test waits for it before
|
`TRACK_PUBLISHED` once the track is published: the Java test waits for it before
|
||||||
asserting, and stops the container when the test ends, so the programs never
|
asserting, and stops the container when the test ends, so the programs never
|
||||||
exit on their own.
|
exit on their own.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue