From 12c9f8f255e19f3354454a8afe1a0502f38cd65f Mon Sep 17 00:00:00 2001 From: Phoenix616 Date: Tue, 12 Mar 2024 13:41:00 +0100 Subject: [PATCH 1/5] Make discovery namespace configurable --- README-JP.md | 1 + README.md | 2 ++ pom.xml | 2 +- src/main/java/net/azisaba/kuvel/Kuvel.java | 6 ++++-- src/main/java/net/azisaba/kuvel/KuvelServiceHandler.java | 5 +++-- src/main/java/net/azisaba/kuvel/config/KuvelConfig.java | 3 +++ .../discovery/impl/redis/RedisLoadBalancerDiscovery.java | 7 ++++--- .../kuvel/discovery/impl/redis/RedisServerDiscovery.java | 5 +++-- .../net/azisaba/kuvel/redis/RedisConnectionLeader.java | 2 ++ src/main/resources/config.yml | 2 ++ 10 files changed, 25 insertions(+), 10 deletions(-) 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..481d20a4 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" 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..07c25070 100644 --- a/src/main/java/net/azisaba/kuvel/Kuvel.java +++ b/src/main/java/net/azisaba/kuvel/Kuvel.java @@ -24,7 +24,7 @@ @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.", @@ -62,7 +62,7 @@ public void onProxyInitialization(ProxyInitializeEvent event) { return; } - kuvelServiceHandler = new KuvelServiceHandler(this, client); + kuvelServiceHandler = new KuvelServiceHandler(this, client, kuvelConfig.getNamespace()); Objects.requireNonNull(kuvelConfig.getRedisConnectionData()); Objects.requireNonNull(kuvelConfig.getProxyGroupName()); @@ -88,6 +88,7 @@ public void onProxyInitialization(ProxyInitializeEvent event) { new RedisLoadBalancerDiscovery( client, this, + kuvelConfig.getNamespace(), kuvelConfig.getRedisConnectionData().createJedisPool(), kuvelConfig.getProxyGroupName(), redisConnectionLeader, @@ -97,6 +98,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..9917e3b3 100644 --- a/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java +++ b/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java @@ -16,6 +16,7 @@ public class KuvelConfig { private static final String CONFIG_FILE_PATH = "./plugins/Kuvel/config.yml"; + @Nullable private String namespace; private boolean redisEnabled; @Nullable private RedisConnectionData redisConnectionData; @Nullable private String proxyGroupName; @@ -24,6 +25,8 @@ public void load() throws IOException { VelocityConfigLoader conf = VelocityConfigLoader.load(new File(CONFIG_FILE_PATH)); conf.saveDefaultConfig(); + namespace = conf.getString("namespace", null); + String hostname = conf.getString("redis.connection.hostname"); int port = conf.getInt("redis.connection.port", -1); String username = conf.getString("redis.connection.username"); 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" From fe164ed637be84800779540a7e5c001b18653af1 Mon Sep 17 00:00:00 2001 From: Phoenix616 Date: Tue, 12 Mar 2024 13:46:24 +0100 Subject: [PATCH 2/5] Use the plugin's DataDirectory instead of a hardcoded path for the config --- src/main/java/net/azisaba/kuvel/Kuvel.java | 8 +++++++- src/main/java/net/azisaba/kuvel/config/KuvelConfig.java | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/azisaba/kuvel/Kuvel.java b/src/main/java/net/azisaba/kuvel/Kuvel.java index 07c25070..84b3f11a 100644 --- a/src/main/java/net/azisaba/kuvel/Kuvel.java +++ b/src/main/java/net/azisaba/kuvel/Kuvel.java @@ -5,9 +5,13 @@ 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; @@ -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 diff --git a/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java b/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java index 9917e3b3..0500d2d7 100644 --- a/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java +++ b/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java @@ -14,7 +14,7 @@ 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; @@ -22,7 +22,7 @@ public class KuvelConfig { @Nullable private String proxyGroupName; public void load() throws IOException { - VelocityConfigLoader conf = VelocityConfigLoader.load(new File(CONFIG_FILE_PATH)); + VelocityConfigLoader conf = VelocityConfigLoader.load(new File(plugin.getDataDirectory(), CONFIG_FILE_NAME)); conf.saveDefaultConfig(); namespace = conf.getString("namespace", null); From 1bbaf2949ede6afc2a94e510e1e9760be174119e Mon Sep 17 00:00:00 2001 From: Phoenix616 Date: Tue, 12 Mar 2024 14:00:01 +0100 Subject: [PATCH 3/5] Add ability to configure plugin via environment variables --- README.md | 4 ++++ .../net/azisaba/kuvel/config/KuvelConfig.java | 22 ++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 481d20a4..f7cbc59f 100644 --- a/README.md +++ b/README.md @@ -31,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/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java b/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java index 0500d2d7..63ff5824 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; @@ -25,12 +26,23 @@ public void load() throws IOException { VelocityConfigLoader conf = VelocityConfigLoader.load(new File(plugin.getDataDirectory(), CONFIG_FILE_NAME)); conf.saveDefaultConfig(); - namespace = conf.getString("namespace", null); + Map env = System.getenv(); - String hostname = conf.getString("redis.connection.hostname"); + 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() + .warning("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; @@ -42,6 +54,6 @@ public void load() throws IOException { 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)); } } From 86eea54913bcdd0f50e1e2e4e10e0c3cec71dde9 Mon Sep 17 00:00:00 2001 From: Phoenix616 Date: Wed, 13 Mar 2024 13:43:09 +0100 Subject: [PATCH 4/5] Use slf4j logger instead of JUL This fixes logging issues in certain environments. Also replace stacktrace printing with logging it. --- src/main/java/net/azisaba/kuvel/Kuvel.java | 5 ++--- src/main/java/net/azisaba/kuvel/config/KuvelConfig.java | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/azisaba/kuvel/Kuvel.java b/src/main/java/net/azisaba/kuvel/Kuvel.java index 84b3f11a..49dc1c54 100644 --- a/src/main/java/net/azisaba/kuvel/Kuvel.java +++ b/src/main/java/net/azisaba/kuvel/Kuvel.java @@ -14,7 +14,6 @@ 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; @@ -24,6 +23,7 @@ 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", @@ -63,8 +63,7 @@ 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; } diff --git a/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java b/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java index 63ff5824..e6c528b0 100644 --- a/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java +++ b/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java @@ -38,7 +38,8 @@ public void load() throws IOException { } catch (NumberFormatException e) { plugin .getLogger() - .warning("Invalid port number for Redis connection specified in KUVEL_REDIS_CONNECTION_PORT environment variable. Using port " + port + " from config.yml."); + .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")); @@ -48,7 +49,7 @@ public void load() throws IOException { 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); From 352dd29ca61bb6e8a530ad2efcd8cdadc6a082a4 Mon Sep 17 00:00:00 2001 From: Phoenix616 Date: Thu, 21 Mar 2024 14:11:28 +0100 Subject: [PATCH 5/5] Rename old uppercase datafolder to lowercase if it existed This should do nothing on file systems which ignore the case --- .../net/azisaba/kuvel/config/KuvelConfig.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java b/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java index e6c528b0..21ca2978 100644 --- a/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java +++ b/src/main/java/net/azisaba/kuvel/config/KuvelConfig.java @@ -23,6 +23,21 @@ public class KuvelConfig { @Nullable private String proxyGroupName; public void load() throws IOException { + 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();