diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 4f993007..334f8259 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -19,7 +19,7 @@ jobs:
contents: read
steps:
- uses: actions/checkout@v4
- - uses: actions/setup-java@v3
+ - uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "adopt"
diff --git a/README-JP.md b/README-JP.md
index 9024b26f..d911ceb9 100644
--- a/README-JP.md
+++ b/README-JP.md
@@ -16,6 +16,7 @@ Pluginは [Releases](https://github.com/AzisabaNetwork/Kuvel/releases/latest)
からダウンロードできます。 `Kuvel.jar` をダウンロードしVelocityに導入してください。ダウンロード後、コンフィグの設定を行ってください。
```yml
+namespace: ""
redis:
group-name: "production" # Redisサーバーが同じかつgroup-nameが同じサーバー間でのみ名前同期が行われます
connection:
diff --git a/README.md b/README.md
index c90c9b82..f7cbc59f 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,8 @@ from [Releases](https://github.com/AzisabaNetwork/Kuvel/releases/latest). Downlo
install it into Velocity plugins directory. Also, you have to fill in the configuration file.
```yml
+# The kubernetes namespace to use for the server discovery.
+namespace: ""
# Server name synchronization by Redis is required in load-balanced environments using multiple Velocity.
redis:
group-name: "production"
@@ -29,6 +31,10 @@ redis:
password: "password"
```
+Alternatively you can use environment variables to configure Kuvel. The environment variable will override
+ the config.yml and are `KUVEL_NAMESPACE`, `KUVEL_REDIS_GROUPNAME`, `KUVEL_REDIS_CONNECTION_HOSTNAME`,
+`KUVEL_REDIS_CONNECTION_PORT`, `KUVEL_REDIS_CONNECTION_USERNAME`, and `KUVEL_REDIS_CONNECTION_PASSWORD`.
+
In order for Kuvel to monitor the server, you must request permission from Kubernetes to allow
Velocity pods discovery Minecraft servers. For Velocity pods, please allow get/list/watch to Pods
and ReplicaSets.
diff --git a/pom.xml b/pom.xml
index a32e7cd6..9face74b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
net.azisaba
Kuvel
- 2.0.2
+ 2.1.0
jar
${project.artifactId}
diff --git a/src/main/java/net/azisaba/kuvel/Kuvel.java b/src/main/java/net/azisaba/kuvel/Kuvel.java
index 3dd7911e..49dc1c54 100644
--- a/src/main/java/net/azisaba/kuvel/Kuvel.java
+++ b/src/main/java/net/azisaba/kuvel/Kuvel.java
@@ -5,12 +5,15 @@
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.plugin.Plugin;
+import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
+
+import java.io.File;
+import java.nio.file.Path;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
-import java.util.logging.Logger;
import lombok.Getter;
import net.azisaba.kuvel.config.KuvelConfig;
import net.azisaba.kuvel.discovery.impl.redis.RedisLoadBalancerDiscovery;
@@ -20,11 +23,12 @@
import net.azisaba.kuvel.redis.ProxyIdProvider;
import net.azisaba.kuvel.redis.RedisConnectionLeader;
import net.azisaba.kuvel.redis.RedisSubscriberExecutor;
+import org.slf4j.Logger;
@Plugin(
id = "kuvel",
name = "Kuvel",
- version = "2.0.2",
+ version = "2.1.0",
url = "https://github.com/AzisabaNetwork/Kuvel",
description =
"Server-discovery Velocity plugin for Minecraft servers running in a Kubernetes cluster.",
@@ -34,6 +38,7 @@ public class Kuvel {
private final ProxyServer proxy;
private final Logger logger;
+ private final File dataDirectory;
private KubernetesClient client;
private KuvelServiceHandler kuvelServiceHandler;
@@ -44,9 +49,10 @@ public class Kuvel {
private KuvelConfig kuvelConfig;
@Inject
- public Kuvel(ProxyServer server, Logger logger) {
+ public Kuvel(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) {
this.proxy = server;
this.logger = logger;
+ this.dataDirectory = dataDirectory.toFile();
}
@Subscribe
@@ -57,12 +63,11 @@ public void onProxyInitialization(ProxyInitializeEvent event) {
try {
kuvelConfig.load();
} catch (Exception e) {
- logger.severe("Failed to load config file. Plugin feature will be disabled.");
- e.printStackTrace();
+ logger.error("Failed to load config file. Plugin feature will be disabled.", e);
return;
}
- kuvelServiceHandler = new KuvelServiceHandler(this, client);
+ kuvelServiceHandler = new KuvelServiceHandler(this, client, kuvelConfig.getNamespace());
Objects.requireNonNull(kuvelConfig.getRedisConnectionData());
Objects.requireNonNull(kuvelConfig.getProxyGroupName());
@@ -88,6 +93,7 @@ public void onProxyInitialization(ProxyInitializeEvent event) {
new RedisLoadBalancerDiscovery(
client,
this,
+ kuvelConfig.getNamespace(),
kuvelConfig.getRedisConnectionData().createJedisPool(),
kuvelConfig.getProxyGroupName(),
redisConnectionLeader,
@@ -97,6 +103,7 @@ public void onProxyInitialization(ProxyInitializeEvent event) {
new RedisServerDiscovery(
client,
this,
+ kuvelConfig.getNamespace(),
kuvelConfig.getRedisConnectionData().createJedisPool(),
kuvelConfig.getProxyGroupName(),
redisConnectionLeader,
diff --git a/src/main/java/net/azisaba/kuvel/KuvelServiceHandler.java b/src/main/java/net/azisaba/kuvel/KuvelServiceHandler.java
index 1c5a07a5..7cdc583c 100644
--- a/src/main/java/net/azisaba/kuvel/KuvelServiceHandler.java
+++ b/src/main/java/net/azisaba/kuvel/KuvelServiceHandler.java
@@ -25,6 +25,7 @@ public class KuvelServiceHandler {
private final Kuvel plugin;
private final KubernetesClient client;
+ private final String namespace;
private final HashMap loadBalancerServerMap = new HashMap<>();
private final UidAndServerNameMap podUidAndServerNameMap = new UidAndServerNameMap();
@@ -125,7 +126,7 @@ private void updateLoadBalancerEndpoints(LoadBalancer loadBalancer) {
List pods =
client
.pods()
- .inAnyNamespace()
+ .inNamespace(namespace)
.withLabel(LabelKeys.ENABLE_SERVER_DISCOVERY.getKey(), "true")
.list()
.getItems();
@@ -251,7 +252,7 @@ public void registerPod(String podUid, String serverName) {
Optional pod =
client
.pods()
- .inAnyNamespace()
+ .inNamespace(namespace)
.withLabel(LabelKeys.ENABLE_SERVER_DISCOVERY.getKey(), "true")
.list()
.getItems()
diff --git a/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java b/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java
index 3d260492..21ca2978 100644
--- a/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java
+++ b/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java
@@ -2,6 +2,7 @@
import java.io.File;
import java.io.IOException;
+import java.util.Map;
import javax.annotation.Nullable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@@ -14,31 +15,61 @@ public class KuvelConfig {
private final Kuvel plugin;
- private static final String CONFIG_FILE_PATH = "./plugins/Kuvel/config.yml";
+ private static final String CONFIG_FILE_NAME = "config.yml";
+ @Nullable private String namespace;
private boolean redisEnabled;
@Nullable private RedisConnectionData redisConnectionData;
@Nullable private String proxyGroupName;
public void load() throws IOException {
- VelocityConfigLoader conf = VelocityConfigLoader.load(new File(CONFIG_FILE_PATH));
+ File uppercaseDataFolder = new File(plugin.getDataDirectory().getParentFile(), "Kuvel");
+ if (uppercaseDataFolder.exists() && !plugin.getDataDirectory().exists()) {
+ if (uppercaseDataFolder.renameTo(plugin.getDataDirectory())) {
+ plugin
+ .getLogger()
+ .info(
+ "Successfully renamed the data folder to use a lowercase name.");
+ } else {
+ plugin
+ .getLogger()
+ .warn(
+ "Failed to rename the data folder to be lowercase. Please manually rename the data folder to 'kuvel'.");
+ }
+ }
+
+ VelocityConfigLoader conf = VelocityConfigLoader.load(new File(plugin.getDataDirectory(), CONFIG_FILE_NAME));
conf.saveDefaultConfig();
- String hostname = conf.getString("redis.connection.hostname");
+ Map env = System.getenv();
+
+ namespace = env.getOrDefault("KUVEL_NAMESPACE", conf.getString("namespace", null));
+
+ String hostname = env.getOrDefault("KUVEL_REDIS_CONNECTION_HOSTNAME", conf.getString("redis.connection.hostname"));
int port = conf.getInt("redis.connection.port", -1);
- String username = conf.getString("redis.connection.username");
- String password = conf.getString("redis.connection.password");
+ if (env.containsKey("KUVEL_REDIS_CONNECTION_PORT")) {
+ try {
+ port = Integer.parseInt(env.get("KUVEL_REDIS_CONNECTION_PORT"));
+ } catch (NumberFormatException e) {
+ plugin
+ .getLogger()
+ .warn(
+ "Invalid port number for Redis connection specified in KUVEL_REDIS_CONNECTION_PORT environment variable. Using port " + port + " from config.yml.");
+ }
+ }
+ String username = env.getOrDefault("KUVEL_REDIS_CONNECTION_USERNAME", conf.getString("redis.connection.username"));
+ String password = env.getOrDefault("KUVEL_REDIS_CONNECTION_PASSWORD", conf.getString("redis.connection.password"));
if (hostname == null || port <= 0) {
redisEnabled = false;
plugin
.getLogger()
- .warning(
+ .warn(
"Redis is enabled, but hostname or port is invalid. Redis sync will be disabled.");
} else {
redisConnectionData = new RedisConnectionData(hostname, port, username, password);
}
- proxyGroupName = conf.getString("redis.group-name", null);
+ proxyGroupName = env.getOrDefault("KUVEL_REDIS_GROUPNAME", conf.getString("redis.group-name", null));
}
}
diff --git a/src/main/java/net/azisaba/kuvel/discovery/impl/redis/RedisLoadBalancerDiscovery.java b/src/main/java/net/azisaba/kuvel/discovery/impl/redis/RedisLoadBalancerDiscovery.java
index c1388eea..75ac9e00 100644
--- a/src/main/java/net/azisaba/kuvel/discovery/impl/redis/RedisLoadBalancerDiscovery.java
+++ b/src/main/java/net/azisaba/kuvel/discovery/impl/redis/RedisLoadBalancerDiscovery.java
@@ -32,6 +32,7 @@ public class RedisLoadBalancerDiscovery implements LoadBalancerDiscovery {
private final KubernetesClient client;
private final Kuvel plugin;
+ private final String namespace;
private final JedisPool jedisPool;
private final String groupName;
private final RedisConnectionLeader redisConnectionLeader;
@@ -55,7 +56,7 @@ public void start() {
client
.apps()
.replicaSets()
- .inAnyNamespace()
+ .inNamespace(namespace)
.withLabel(LabelKeys.ENABLE_SERVER_DISCOVERY.getKey(), "true")
.withLabel(LabelKeys.PREFERRED_SERVER_NAME.getKey())
.list()
@@ -241,7 +242,7 @@ public void registerLoadBalancersForStartup() {
client
.apps()
.replicaSets()
- .inAnyNamespace()
+ .inNamespace(namespace)
.withLabel(LabelKeys.ENABLE_SERVER_DISCOVERY.getKey(), "true")
.withLabel(LabelKeys.PREFERRED_SERVER_NAME.getKey())
.list()
@@ -272,7 +273,7 @@ private ReplicaSet getReplicaSetFromUid(String uid) {
return client
.apps()
.replicaSets()
- .inAnyNamespace()
+ .inNamespace(namespace)
.withLabel(LabelKeys.ENABLE_SERVER_DISCOVERY.getKey(), "true")
.withLabel(LabelKeys.PREFERRED_SERVER_NAME.getKey())
.list()
diff --git a/src/main/java/net/azisaba/kuvel/discovery/impl/redis/RedisServerDiscovery.java b/src/main/java/net/azisaba/kuvel/discovery/impl/redis/RedisServerDiscovery.java
index a93b43e2..e9ae8ac8 100644
--- a/src/main/java/net/azisaba/kuvel/discovery/impl/redis/RedisServerDiscovery.java
+++ b/src/main/java/net/azisaba/kuvel/discovery/impl/redis/RedisServerDiscovery.java
@@ -32,6 +32,7 @@ public class RedisServerDiscovery implements ServerDiscovery {
private final KubernetesClient client;
private final Kuvel plugin;
+ private final String namespace;
private final JedisPool jedisPool;
private final String groupName;
private final RedisConnectionLeader redisConnectionLeader;
@@ -52,7 +53,7 @@ public void start() {
List podList =
client
.pods()
- .inAnyNamespace()
+ .inNamespace(namespace)
.withLabel(LabelKeys.ENABLE_SERVER_DISCOVERY.getKey(), "true")
.list()
.getItems();
@@ -113,7 +114,7 @@ public HashMap getServersForStartup() {
client
.pods()
- .inAnyNamespace()
+ .inNamespace(namespace)
.withLabel(LabelKeys.ENABLE_SERVER_DISCOVERY.getKey(), "true")
.withField("status.phase", "Running")
.list()
diff --git a/src/main/java/net/azisaba/kuvel/redis/RedisConnectionLeader.java b/src/main/java/net/azisaba/kuvel/redis/RedisConnectionLeader.java
index 0444a6f4..187784d2 100644
--- a/src/main/java/net/azisaba/kuvel/redis/RedisConnectionLeader.java
+++ b/src/main/java/net/azisaba/kuvel/redis/RedisConnectionLeader.java
@@ -120,6 +120,7 @@ private void runDiscoveryTask() {
new RedisLoadBalancerDiscovery(
plugin.getClient(),
plugin,
+ plugin.getKuvelConfig().getNamespace(),
plugin.getKuvelConfig().getRedisConnectionData().createJedisPool(),
plugin.getKuvelConfig().getProxyGroupName(),
this,
@@ -131,6 +132,7 @@ private void runDiscoveryTask() {
new RedisServerDiscovery(
plugin.getClient(),
plugin,
+ plugin.getKuvelConfig().getNamespace(),
plugin.getKuvelConfig().getRedisConnectionData().createJedisPool(),
plugin.getKuvelConfig().getProxyGroupName(),
this,
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 1957e0d2..bfe0c060 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -1,3 +1,5 @@
+# The kubernetes namespace to use for the server discovery.
+namespace: ""
# Server name synchronization by Redis is required in load-balanced environments using multiple Velocity.
redis:
group-name: "production"