From 01e1e0a00b2cb2c0b88c40e6246b8544c579e023 Mon Sep 17 00:00:00 2001 From: Quan Tran Date: Fri, 28 Feb 2025 16:16:37 +0700 Subject: [PATCH 1/2] Use Apache Kvrocks to back the standalone RedisExtension --- .../james/backends/redis/DockerKvrocks.java | 110 ++++++++++++++++++ .../james/backends/redis/RedisExtension.java | 4 +- 2 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 backends-common/redis/src/test/java/org/apache/james/backends/redis/DockerKvrocks.java diff --git a/backends-common/redis/src/test/java/org/apache/james/backends/redis/DockerKvrocks.java b/backends-common/redis/src/test/java/org/apache/james/backends/redis/DockerKvrocks.java new file mode 100644 index 00000000000..a9740021b61 --- /dev/null +++ b/backends-common/redis/src/test/java/org/apache/james/backends/redis/DockerKvrocks.java @@ -0,0 +1,110 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you 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.apache.james.backends.redis; + +import static java.lang.Boolean.TRUE; + +import java.net.URI; +import java.time.Duration; +import java.util.UUID; + +import org.apache.http.client.utils.URIBuilder; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.wait.strategy.DockerHealthcheckWaitStrategy; +import org.testcontainers.utility.DockerImageName; + +import com.github.fge.lambdas.Throwing; + +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.sync.RedisCommands; + +public class DockerKvrocks extends DockerRedis { + public static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("apache/kvrocks").withTag("2.11.1"); + public static final int DEFAULT_PORT = 6666; + + private final GenericContainer container; + + public DockerKvrocks() { + this.container = getContainer(); + } + + public DockerKvrocks(Network network) { + this.container = getContainer() + .withNetwork(network); + } + + private GenericContainer getContainer() { + return new GenericContainer<>(DEFAULT_IMAGE_NAME) + .withExposedPorts(DEFAULT_PORT) + .withCreateContainerCmdModifier(createContainerCmd -> createContainerCmd.withName("james-kvrocks-test-" + UUID.randomUUID())) + .withNetworkAliases("redis") + .waitingFor(new DockerHealthcheckWaitStrategy() + .withStartupTimeout(Duration.ofMinutes(2))); + } + + @Override + public URI redisURI() { + return Throwing.supplier(() -> new URIBuilder() + .setScheme("redis") + .setHost(container.getHost()) + .setPort(container.getMappedPort(DEFAULT_PORT)) + .build()).get(); + } + + @Override + public void start() { + if (!container.isRunning()) { + container.start(); + } + } + + @Override + public void stop() { + container.stop(); + } + + @Override + public void pause() { + container.getDockerClient().pauseContainerCmd(container.getContainerId()).exec(); + } + + @Override + public void unPause() { + container.getDockerClient().unpauseContainerCmd(container.getContainerId()).exec(); + } + + @Override + public boolean isPaused() { + return TRUE.equals(container.getDockerClient().inspectContainerCmd(container.getContainerId()) + .exec() + .getState() + .getPaused()); + } + + public RedisCommands createClient() { + return RedisClient.create(redisURI().toString()) + .connect().sync(); + } + + public void flushAll() { + createClient().flushall(); + } +} diff --git a/backends-common/redis/src/test/java/org/apache/james/backends/redis/RedisExtension.java b/backends-common/redis/src/test/java/org/apache/james/backends/redis/RedisExtension.java index d90e636269e..6a5dbe7098b 100644 --- a/backends-common/redis/src/test/java/org/apache/james/backends/redis/RedisExtension.java +++ b/backends-common/redis/src/test/java/org/apache/james/backends/redis/RedisExtension.java @@ -31,7 +31,7 @@ import com.google.inject.Provides; public class RedisExtension implements GuiceModuleTestExtension { - private static final DockerRedis DOCKER_REDIS_SINGLETON = new DockerRedis(); + private static final DockerKvrocks DOCKER_REDIS_SINGLETON = new DockerKvrocks(); @Override public void beforeAll(ExtensionContext extensionContext) { @@ -59,7 +59,7 @@ public RedisConfiguration provideConfig() { }; } - public DockerRedis dockerRedis() { + public DockerKvrocks dockerRedis() { return DOCKER_REDIS_SINGLETON; } From ef15c2533ca383183228308b01fdfea9aa018c4e Mon Sep 17 00:00:00 2001 From: Quan Tran Date: Fri, 28 Feb 2025 16:18:14 +0700 Subject: [PATCH 2/2] Use Apache Kvrocks to back Rspamd storage --- .../java/org/apache/james/rspamd/RspamdExtension.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/third-party/rspamd/src/test/java/org/apache/james/rspamd/RspamdExtension.java b/third-party/rspamd/src/test/java/org/apache/james/rspamd/RspamdExtension.java index ef0d9fdba5d..df47c0129b3 100644 --- a/third-party/rspamd/src/test/java/org/apache/james/rspamd/RspamdExtension.java +++ b/third-party/rspamd/src/test/java/org/apache/james/rspamd/RspamdExtension.java @@ -41,10 +41,10 @@ public class RspamdExtension implements GuiceModuleTestExtension { public static final String PASSWORD = "admin"; private static final DockerImageName RSPAMD_IMAGE = DockerImageName.parse("rspamd/rspamd").withTag("3.9.1"); - private static final DockerImageName REDIS_IMAGE = DockerImageName.parse("redis").withTag("7.2.5"); + private static final DockerImageName REDIS_IMAGE = DockerImageName.parse("apache/kvrocks").withTag("2.11.1"); private static final DockerImageName CLAMAV_IMAGE = DockerImageName.parse("clamav/clamav").withTag("1.3"); private static final int RSPAMD_DEFAULT_PORT = 11334; - private static final int REDIS_DEFAULT_PORT = 6379; + private static final int REDIS_DEFAULT_PORT = 6666; private static final int CLAMAV_DEFAULT_PORT = 3310; private final GenericContainer rspamdContainer; @@ -80,7 +80,7 @@ public void beforeEach(ExtensionContext extensionContext) { public GenericContainer rspamdContainer(Network network) { return new GenericContainer<>(RSPAMD_IMAGE) .withExposedPorts(RSPAMD_DEFAULT_PORT) - .withEnv("RSPAMD_REDIS_SERVERS", "redis") + .withEnv("RSPAMD_REDIS_SERVERS", "redis:6666") .withEnv("RSPAMD_CLAMAV_SERVERS", "clamav") .withEnv("RSPAMD_PASSWORD", PASSWORD) .withCopyFileToContainer(MountableFile.forClasspathResource("rspamd-config/antivirus.conf"), "/etc/rspamd/override.d/antivirus.conf") @@ -119,7 +119,7 @@ public GenericContainer clamAVContainer(Network network) { public void redisFlushAll() { try { - redisContainer.execInContainer("redis-cli", "flushall"); + redisContainer.execInContainer("redis-cli", "-p", "6666", "flushall"); } catch (IOException | InterruptedException e) { throw new RuntimeException(e); }