diff --git a/README-JP.md b/README-JP.md index 984ff929..335c9928 100644 --- a/README-JP.md +++ b/README-JP.md @@ -197,9 +197,11 @@ Kubernetesクラスター内ではPodがほぼ同時に作成されることが - `/kuvel status` - 登録状況と不足登録数を表示 - `/kuvel list ` - 現在のUIDとサーバー名の対応一覧を表示 - `/kuvel register ` - Pod UIDを指定して登録 +- `/kuvel register loadbalancer ` - ReplicaSet UIDを指定してLoadBalancerを登録 - `/kuvel unregister ` - Pod UIDを指定して登録解除 +- `/kuvel unregister loadbalancer ` - ReplicaSet UIDを指定してLoadBalancerを登録解除 - `/kuvel setname ` - Podの登録名を変更 -- `/kuvel repair` - 不足登録の再登録と壊れた対応の整理を実行 +- `/kuvel repair` - 不足登録(Pod/LoadBalancer)の再登録と壊れた対応の整理を実行 ## ライセンス diff --git a/README.md b/README.md index b7dfba0d..76c05451 100644 --- a/README.md +++ b/README.md @@ -198,9 +198,11 @@ Use `/kuvel` to inspect, update, and repair server registration state. - `/kuvel status` - show registration summary and missing registrations - `/kuvel list ` - list UID to server-name mappings - `/kuvel register ` - register a pod by UID +- `/kuvel register loadbalancer ` - register a load balancer by ReplicaSet UID - `/kuvel unregister ` - unregister a pod by UID +- `/kuvel unregister loadbalancer ` - unregister a load balancer by ReplicaSet UID - `/kuvel setname ` - change a pod's registered server name -- `/kuvel repair` - re-register missing entries and clean broken mappings +- `/kuvel repair` - re-register missing entries (pods/load balancers) and clean broken mappings ## License [GNU General Public License v3.0](LICENSE) diff --git a/src/main/java/net/azisaba/kuvel/KuvelServiceHandler.java b/src/main/java/net/azisaba/kuvel/KuvelServiceHandler.java index e039f6b2..2900caf8 100644 --- a/src/main/java/net/azisaba/kuvel/KuvelServiceHandler.java +++ b/src/main/java/net/azisaba/kuvel/KuvelServiceHandler.java @@ -5,6 +5,8 @@ import io.fabric8.kubernetes.api.model.ContainerPort; import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.PodList; +import io.fabric8.kubernetes.api.model.apps.ReplicaSet; +import io.fabric8.kubernetes.api.model.apps.ReplicaSetList; import io.fabric8.kubernetes.client.KubernetesClient; import java.net.InetSocketAddress; import java.util.ArrayList; @@ -17,11 +19,13 @@ import io.fabric8.kubernetes.client.dsl.FilterWatchListDeletable; import io.fabric8.kubernetes.client.dsl.PodResource; +import io.fabric8.kubernetes.client.dsl.RollableScalableResource; import lombok.Getter; import lombok.RequiredArgsConstructor; import net.azisaba.kuvel.discovery.LoadBalancerDiscovery; import net.azisaba.kuvel.discovery.ServerDiscovery; import net.azisaba.kuvel.loadbalancer.LoadBalancer; +import net.azisaba.kuvel.loadbalancer.strategy.impl.RoundRobinLoadBalancingStrategy; import net.azisaba.kuvel.util.LabelKeys; import net.azisaba.kuvel.util.UidAndServerNameMap; @@ -337,4 +341,63 @@ public void unregisterPod(Pod pod) { public boolean isPodRegistered(String podId) { return podUidAndServerNameMap.getServerNameFromUid(podId) != null; } + + /** + * Register a load balancer with ReplicaSet uid and server name. + * + * @param replicaSetUid The ReplicaSet uid to register. + * @param serverName The server name to register. + * @return true if the load balancer is registered successfully. + */ + public boolean registerLoadBalancer(String replicaSetUid, String serverName) { + FilterWatchListDeletable> request = + client.apps().replicaSets().inNamespace(namespace); + + for (Entry e : plugin.getKuvelConfig().getLabelSelectors().entrySet()) { + request = request.withLabel(e.getKey(), e.getValue()); + } + + Optional replicaSet = + request + .list() + .getItems() + .stream() + .filter(r -> r.getMetadata().getUid().equals(replicaSetUid)) + .findFirst(); + if (replicaSet.isEmpty()) { + return false; + } + + boolean initialServer = + replicaSet + .get() + .getMetadata() + .getLabels() + .getOrDefault( + LabelKeys.INITIAL_SERVER.getKey(plugin.getKuvelConfig().getLabelKeyPrefix()), + "false") + .equalsIgnoreCase("true"); + + plugin + .getProxy() + .registerServer(new ServerInfo(serverName, new InetSocketAddress("0.0.0.0", 0))); + registerLoadBalancer( + new LoadBalancer( + plugin.getProxy(), + plugin.getProxy().getServer(serverName).orElseThrow(), + new RoundRobinLoadBalancingStrategy(), + replicaSetUid, + initialServer)); + return true; + } + + /** + * Gets whether the specified load balancer is registered. + * + * @param replicaSetUid The ReplicaSet uid to check. + * @return true if the specified load balancer uid is registered. + */ + public boolean isLoadBalancerRegistered(String replicaSetUid) { + return replicaSetUidAndServerNameMap.getServerNameFromUid(replicaSetUid) != null; + } } diff --git a/src/main/java/net/azisaba/kuvel/command/KuvelCommand.java b/src/main/java/net/azisaba/kuvel/command/KuvelCommand.java index 0051abe7..bb831beb 100644 --- a/src/main/java/net/azisaba/kuvel/command/KuvelCommand.java +++ b/src/main/java/net/azisaba/kuvel/command/KuvelCommand.java @@ -93,6 +93,32 @@ public void execute(Invocation invocation) { } if (args[0].equalsIgnoreCase("register")) { + if (args.length >= 2 && args[1].equalsIgnoreCase("loadbalancer")) { + if (args.length < 4) { + source.sendMessage( + Component.text("Usage: /kuvel register loadbalancer ")); + return; + } + String mappedUid = handler.getReplicaSetUidAndServerNameMap().getUidFromServerName(args[3]); + if (mappedUid != null && !mappedUid.equals(args[2])) { + source.sendMessage( + Component.text("Server name is already mapped to another load balancer uid: " + mappedUid)); + return; + } + if (plugin.getProxy().getServer(args[3]).isPresent() && mappedUid == null) { + source.sendMessage(Component.text("Server name is already registered: " + args[3])); + return; + } + + if (handler.registerLoadBalancer(args[2], args[3])) { + source.sendMessage( + Component.text("Registered load balancer mapping: " + args[2] + " -> " + args[3])); + } else { + source.sendMessage(Component.text("Failed to register load balancer mapping.")); + } + return; + } + if (args.length < 3) { source.sendMessage(Component.text("Usage: /kuvel register ")); return; @@ -117,6 +143,23 @@ public void execute(Invocation invocation) { } if (args[0].equalsIgnoreCase("unregister")) { + if (args.length >= 2 && args[1].equalsIgnoreCase("loadbalancer")) { + if (args.length < 3) { + source.sendMessage(Component.text("Usage: /kuvel unregister loadbalancer ")); + return; + } + + if (!handler.isLoadBalancerRegistered(args[2])) { + source.sendMessage( + Component.text("Load balancer uid is not currently registered: " + args[2])); + return; + } + + handler.unregisterLoadBalancer(args[2]); + source.sendMessage(Component.text("Unregistered load balancer mapping: " + args[2])); + return; + } + if (args.length < 2) { source.sendMessage(Component.text("Usage: /kuvel unregister ")); return; @@ -187,13 +230,19 @@ public void execute(Invocation invocation) { } int cleanedLoadBalancerCount = 0; + int repairedLoadBalancerCount = 0; for (Map.Entry entry : handler.getReplicaSetUidAndServerNameMap().getAllMap().entrySet()) { if (plugin.getProxy().getServer(entry.getValue()).isPresent()) { continue; } - handler.unregisterLoadBalancer(entry.getKey()); - cleanedLoadBalancerCount++; + boolean success = handler.registerLoadBalancer(entry.getKey(), entry.getValue()); + if (success) { + repairedLoadBalancerCount++; + } else { + handler.unregisterLoadBalancer(entry.getKey()); + cleanedLoadBalancerCount++; + } } source.sendMessage( @@ -202,6 +251,8 @@ public void execute(Invocation invocation) { + repairedPodCount + ", cleanedPod=" + cleanedPodCount + + ", repairedLoadBalancer=" + + repairedLoadBalancerCount + ", cleanedLoadBalancer=" + cleanedLoadBalancerCount)); return; @@ -214,7 +265,9 @@ private void sendHelp(CommandSource source) { source.sendMessage(Component.text("/kuvel status")); source.sendMessage(Component.text("/kuvel list ")); source.sendMessage(Component.text("/kuvel register ")); + source.sendMessage(Component.text("/kuvel register loadbalancer ")); source.sendMessage(Component.text("/kuvel unregister ")); + source.sendMessage(Component.text("/kuvel unregister loadbalancer ")); source.sendMessage(Component.text("/kuvel setname ")); source.sendMessage(Component.text("/kuvel repair")); }