From 9f0f7c374a75c97f9ee7348886410e9b5b2d17f5 Mon Sep 17 00:00:00 2001 From: benbierens Date: Wed, 8 Nov 2023 10:19:06 +0100 Subject: [PATCH 01/25] port update --- Tools/CodexNetDeployer/deploy-public-testnet.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tools/CodexNetDeployer/deploy-public-testnet.sh b/Tools/CodexNetDeployer/deploy-public-testnet.sh index 3b4178a..159315a 100644 --- a/Tools/CodexNetDeployer/deploy-public-testnet.sh +++ b/Tools/CodexNetDeployer/deploy-public-testnet.sh @@ -17,11 +17,11 @@ dotnet run \ \ --public-testnet=1 \ --public-ip=1.2.3.4 \ - --public-discports=20010,20020,20030 \ - --public-listenports=20011,20021,20031 \ + --public-discports=30010,30020,30030 \ + --public-listenports=30011,30021,30031 \ --public-gethip=1.2.3.5 \ - --public-gethdiscport=20040 \ - --public-gethlistenport=20041 \ + --public-gethdiscport=30040 \ + --public-gethlistenport=30041 \ \ --discord-bot=1 \ --dbot-token=tokenhere \ From 8d12bc45a1ba386c2ad890e1f006701ef5929d30 Mon Sep 17 00:00:00 2001 From: benbierens Date: Wed, 8 Nov 2023 11:49:21 +0100 Subject: [PATCH 02/25] Loops in kubeconfig and namespace for discord bot. --- .../DiscordBotContainerRecipe.cs | 4 ++++ .../DiscordBotStartupConfig.cs | 4 +++- Tools/BiblioTech/Configuration.cs | 6 ++++++ Tools/BiblioTech/DeploymentsFilesMonitor.cs | 13 ++++++++++++- Tools/BiblioTech/Program.cs | 4 ++-- Tools/CodexNetDeployer/Deployer.cs | 3 ++- 6 files changed, 29 insertions(+), 5 deletions(-) diff --git a/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotContainerRecipe.cs b/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotContainerRecipe.cs index c65dd9c..a598d78 100644 --- a/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotContainerRecipe.cs +++ b/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotContainerRecipe.cs @@ -16,12 +16,16 @@ namespace CodexDiscordBotPlugin AddEnvVar("SERVERNAME", config.ServerName); AddEnvVar("ADMINROLE", config.AdminRoleName); AddEnvVar("ADMINCHANNELNAME", config.AdminChannelName); + AddEnvVar("KUBECONFIG", "/opt/kubeconfig.yaml"); + AddEnvVar("KUBENAMESPACE", config.KubeNamespace); if (!string.IsNullOrEmpty(config.DataPath)) { AddEnvVar("DATAPATH", config.DataPath); AddVolume(config.DataPath, 1.GB()); } + + AddVolume(name: "kubeconfig", mountPath: "/opt/kubeconfig.yaml", subPath: "kubeconfig.yaml", secret: "codex-dist-tests-app-kubeconfig"); } } } diff --git a/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotStartupConfig.cs b/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotStartupConfig.cs index bb00a8d..e590cfa 100644 --- a/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotStartupConfig.cs +++ b/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotStartupConfig.cs @@ -2,13 +2,14 @@ { public class DiscordBotStartupConfig { - public DiscordBotStartupConfig(string name, string token, string serverName, string adminRoleName, string adminChannelName) + public DiscordBotStartupConfig(string name, string token, string serverName, string adminRoleName, string adminChannelName, string kubeNamespace) { Name = name; Token = token; ServerName = serverName; AdminRoleName = adminRoleName; AdminChannelName = adminChannelName; + KubeNamespace = kubeNamespace; } public string Name { get; } @@ -16,6 +17,7 @@ public string ServerName { get; } public string AdminRoleName { get; } public string AdminChannelName { get; } + public string KubeNamespace { get; } public string? DataPath { get; set; } } } diff --git a/Tools/BiblioTech/Configuration.cs b/Tools/BiblioTech/Configuration.cs index 10e134e..eb58870 100644 --- a/Tools/BiblioTech/Configuration.cs +++ b/Tools/BiblioTech/Configuration.cs @@ -19,6 +19,12 @@ namespace BiblioTech [Uniform("admin-channel-name", "ac", "ADMINCHANNELNAME", true, "Name of the Discord server channel where admin commands are allowed.")] public string AdminChannelName { get; set; } = "admin-channel"; + [Uniform("kube-config", "kc", "KUBECONFIG", true, "Path to Kubeconfig file. Use a Kubeconfig with read-only access.")] + public string KubeConfigFile { get; set; } = "null"; + + [Uniform("kube-namespace", "kn", "KUBENAMESPACE", true, "Kubernetes namespace.")] + public string KubeNamespace { get; set; } = string.Empty; + public string EndpointsPath { get diff --git a/Tools/BiblioTech/DeploymentsFilesMonitor.cs b/Tools/BiblioTech/DeploymentsFilesMonitor.cs index 9278574..0092034 100644 --- a/Tools/BiblioTech/DeploymentsFilesMonitor.cs +++ b/Tools/BiblioTech/DeploymentsFilesMonitor.cs @@ -28,7 +28,7 @@ namespace BiblioTech try { var deploy = JsonConvert.DeserializeObject(str); - if (deploy != null) + if (IsDeploymentOk(deploy)) { var targetFile = Path.Combine(Program.Config.EndpointsPath, Guid.NewGuid().ToString().ToLowerInvariant() + ".json"); File.WriteAllText(targetFile, str); @@ -59,6 +59,17 @@ namespace BiblioTech return false; } + private bool IsDeploymentOk(CodexDeployment? deploy) + { + if (deploy == null) return false; + if (deploy.CodexInstances == null) return false; + if (!deploy.CodexInstances.Any()) return false; + if (!deploy.CodexInstances.All(i => i.Containers != null && i.Info != null)) return false; + if (deploy.GethDeployment == null) return false; + if (deploy.GethDeployment.Containers == null) return false; + return true; + } + private void LoadDeployments() { var path = Program.Config.EndpointsPath; diff --git a/Tools/BiblioTech/Program.cs b/Tools/BiblioTech/Program.cs index ecd9147..4bb1175 100644 --- a/Tools/BiblioTech/Program.cs +++ b/Tools/BiblioTech/Program.cs @@ -41,10 +41,10 @@ namespace BiblioTech ProjectPlugin.Load(); var entryPoint = new EntryPoint(new ConsoleLog(), new KubernetesWorkflow.Configuration( - kubeConfigFile: null, + kubeConfigFile: Config.KubeConfigFile, operationTimeout: TimeSpan.FromMinutes(5), retryDelay: TimeSpan.FromSeconds(10), - kubernetesNamespace: "not-applicable"), "datafiles"); + kubernetesNamespace: Config.KubeNamespace), "datafiles"); var ci = entryPoint.CreateInterface(); diff --git a/Tools/CodexNetDeployer/Deployer.cs b/Tools/CodexNetDeployer/Deployer.cs index ff2dd6f..60415ac 100644 --- a/Tools/CodexNetDeployer/Deployer.cs +++ b/Tools/CodexNetDeployer/Deployer.cs @@ -131,7 +131,8 @@ namespace CodexNetDeployer token: config.DiscordBotToken, serverName: config.DiscordBotServerName, adminRoleName: config.DiscordBotAdminRoleName, - adminChannelName: config.DiscordBotAdminChannelName) + adminChannelName: config.DiscordBotAdminChannelName, + kubeNamespace: config.KubeNamespace) { DataPath = config.DiscordBotDataPath }); From d1403c04c4d2e8413066d0fe72392c8bb2471dd8 Mon Sep 17 00:00:00 2001 From: benbierens Date: Wed, 8 Nov 2023 11:59:31 +0100 Subject: [PATCH 03/25] updates bot secret name --- .../CodexDiscordBotPlugin/DiscordBotContainerRecipe.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotContainerRecipe.cs b/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotContainerRecipe.cs index a598d78..fbf0ea7 100644 --- a/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotContainerRecipe.cs +++ b/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotContainerRecipe.cs @@ -25,7 +25,7 @@ namespace CodexDiscordBotPlugin AddVolume(config.DataPath, 1.GB()); } - AddVolume(name: "kubeconfig", mountPath: "/opt/kubeconfig.yaml", subPath: "kubeconfig.yaml", secret: "codex-dist-tests-app-kubeconfig"); + AddVolume(name: "kubeconfig", mountPath: "/opt/kubeconfig.yaml", subPath: "kubeconfig.yaml", secret: "discordbot-sa-kubeconfig"); } } } From 180bf3fd35b0dfd9e57a245ddb1de999f233e52f Mon Sep 17 00:00:00 2001 From: benbierens Date: Wed, 8 Nov 2023 13:33:48 +0100 Subject: [PATCH 04/25] Fixes debugPeer response formatting --- Tools/BiblioTech/Commands/AdminCommand.cs | 37 ++++++++++------------ Tools/BiblioTech/Commands/SprCommand.cs | 2 +- Tools/BiblioTech/Options/CommandContext.cs | 8 ++++- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Tools/BiblioTech/Commands/AdminCommand.cs b/Tools/BiblioTech/Commands/AdminCommand.cs index d35f937..4ad3a3c 100644 --- a/Tools/BiblioTech/Commands/AdminCommand.cs +++ b/Tools/BiblioTech/Commands/AdminCommand.cs @@ -110,15 +110,7 @@ namespace BiblioTech.Commands } var report = string.Join(Environment.NewLine, Program.UserRepo.GetInteractionReport(user)); - if (report.Length > 1900) - { - var filename = $"user-{user.Username}.log"; - await context.FollowupWithAttachement(filename, report); - } - else - { - await context.Followup(report); - } + await context.Followup(report); } } @@ -293,7 +285,8 @@ namespace BiblioTech.Commands var nl = Environment.NewLine; var content = new List { - $"{DateTime.UtcNow.ToString("o")} - {group.Count()} Codex nodes." + $"{DateTime.UtcNow.ToString("o")} - {group.Count()} Codex nodes.", + $"Deployment name: '{name}'" }; foreach (var node in group) @@ -311,8 +304,7 @@ namespace BiblioTech.Commands } } - var filename = $"netinfo-{NoWhitespaces(name)}.log"; - await context.FollowupWithAttachement(filename, string.Join(nl, content.ToArray())); + await context.Followup(string.Join(nl, content)); }); } } @@ -336,29 +328,32 @@ namespace BiblioTech.Commands await OnDeployment(context, async (group, name) => { - await context.Followup($"Calling debug/peer for '{peerId}' on {group.Count()} Codex nodes."); + var nl = Environment.NewLine; + var content = new List + { + $"{DateTime.UtcNow.ToString("o")} - {group.Count()} Codex nodes.", + $"Deployment name: '{name}'" + }; + + content.Add($"Calling debug/peer for '{peerId}' on {group.Count()} Codex nodes."); foreach (var node in group) { try { var info = node.GetDebugPeer(peerId); - var nl = Environment.NewLine; var json = JsonConvert.SerializeObject(info, Formatting.Indented); var jsonInsert = $"{nl}```{nl}{json}{nl}```{nl}"; - await context.Followup($"Node '{node.GetName()}' responded with {jsonInsert}"); + content.Add($"Node '{node.GetName()}' responded with {jsonInsert}"); } catch (Exception ex) { - await context.Followup($"Node '{node.GetName()}' failed to respond with exception: " + ex); + content.Add($"Node '{node.GetName()}' failed to respond with exception: " + ex); } } + + await context.Followup(string.Join(nl, content)); }); } } - - private static string NoWhitespaces(string s) - { - return s.Replace(" ", "-"); - } } } diff --git a/Tools/BiblioTech/Commands/SprCommand.cs b/Tools/BiblioTech/Commands/SprCommand.cs index 6d52b3d..ed19c7f 100644 --- a/Tools/BiblioTech/Commands/SprCommand.cs +++ b/Tools/BiblioTech/Commands/SprCommand.cs @@ -50,7 +50,7 @@ namespace BiblioTech.Commands var i = random.Next(0, sprCache.Count); var spr = sprCache[i]; - await context.Followup($"Your SPR: '{spr}'"); + await context.Followup($"Your SPR: `{spr}`"); } private bool ShouldUpdate() diff --git a/Tools/BiblioTech/Options/CommandContext.cs b/Tools/BiblioTech/Options/CommandContext.cs index 01567ce..d3293e1 100644 --- a/Tools/BiblioTech/Options/CommandContext.cs +++ b/Tools/BiblioTech/Options/CommandContext.cs @@ -15,13 +15,19 @@ namespace BiblioTech.Options public async Task Followup(string message) { + if (message.Length > 1900) + { + await FollowupWithAttachement("codexdiscordbot_file.txt", message); + return; + } + await Command.ModifyOriginalResponseAsync(m => { m.Content = message; }); } - public async Task FollowupWithAttachement(string filename, string content) + private async Task FollowupWithAttachement(string filename, string content) { using var fileStream = new MemoryStream(); using var streamWriter = new StreamWriter(fileStream); From ef83441b2f15cd59602f904618bc8c0c2c614f9c Mon Sep 17 00:00:00 2001 From: benbierens Date: Thu, 9 Nov 2023 10:13:34 +0100 Subject: [PATCH 05/25] Correct chunking of long messages --- Tools/BiblioTech/BaseCommand.cs | 10 +- Tools/BiblioTech/Commands/AdminCommand.cs | 122 ++++++++++++--------- Tools/BiblioTech/Options/CommandContext.cs | 87 +++++++++++++-- Tools/BiblioTech/UserRepo.cs | 23 ++-- 4 files changed, 169 insertions(+), 73 deletions(-) diff --git a/Tools/BiblioTech/BaseCommand.cs b/Tools/BiblioTech/BaseCommand.cs index c68aaeb..45f6ea6 100644 --- a/Tools/BiblioTech/BaseCommand.cs +++ b/Tools/BiblioTech/BaseCommand.cs @@ -29,7 +29,15 @@ namespace BiblioTech } catch (Exception ex) { - await command.FollowupAsync("Something failed while trying to do that...", ephemeral: true); + if (IsInAdminChannel(command)) + { + var msg = "Failed with exception: " + ex; + await command.FollowupAsync(msg.Substring(0, Math.Min(1900, msg.Length))); + } + else + { + await command.FollowupAsync("Something failed while trying to do that...", ephemeral: true); + } Console.WriteLine(ex); } } diff --git a/Tools/BiblioTech/Commands/AdminCommand.cs b/Tools/BiblioTech/Commands/AdminCommand.cs index 4ad3a3c..9def408 100644 --- a/Tools/BiblioTech/Commands/AdminCommand.cs +++ b/Tools/BiblioTech/Commands/AdminCommand.cs @@ -109,7 +109,7 @@ namespace BiblioTech.Commands return; } - var report = string.Join(Environment.NewLine, Program.UserRepo.GetInteractionReport(user)); + var report = Program.UserRepo.GetInteractionReport(user); await context.Followup(report); } } @@ -134,7 +134,7 @@ namespace BiblioTech.Commands } var nl = Environment.NewLine; - await context.Followup($"Deployments:{nl}{string.Join(nl, deployments.Select(FormatDeployment))}"); + await context.Followup(deployments.Select(FormatDeployment).ToArray()); } private string FormatDeployment(CodexDeployment deployment) @@ -249,23 +249,31 @@ namespace BiblioTech.Commands this.ci = ci; } - protected async Task OnDeployment(CommandContext context, Func action) + protected async Task OnDeployment(CommandContext context, Func action) { var deployment = Program.DeploymentFilesMonitor.GetDeployments().SingleOrDefault(); if (deployment == null) { await context.Followup("No deployment found."); - return; + return default; } try { var group = ci.WrapCodexContainers(deployment.CodexInstances.Select(i => i.Containers).ToArray()); - await action(group, deployment.Metadata.Name); + var result = action(group, deployment.Metadata.Name); + return result; } catch (Exception ex) { - await context.Followup("Failed to wrap nodes with exception: " + ex); + var message = new[] + { + "Failed to wrap nodes with exception: " + }; + var exceptionMessage = ex.ToString().Split(Environment.NewLine); + + await context.Followup(message.Concat(exceptionMessage).ToArray()); + return default; } } } @@ -280,32 +288,40 @@ namespace BiblioTech.Commands protected override async Task onSubCommand(CommandContext context) { - await OnDeployment(context, async (group, name) => + var report = await OnDeployment(context, CreateNetInfoReport); + if (report != null && report.Any()) { - var nl = Environment.NewLine; - var content = new List - { - $"{DateTime.UtcNow.ToString("o")} - {group.Count()} Codex nodes.", - $"Deployment name: '{name}'" - }; + await context.Followup(report); + } + } - foreach (var node in group) + private string[] CreateNetInfoReport(ICodexNodeGroup group, string name) + { + var content = new List + { + $"{DateTime.UtcNow.ToString("o")} - {group.Count()} Codex nodes.", + $"Deployment name: '{name}'." + }; + + foreach (var node in group) + { + try { - try - { - var info = node.GetDebugInfo(); - var json = JsonConvert.SerializeObject(info, Formatting.Indented); - var jsonInsert = $"{nl}```{nl}{json}{nl}```{nl}"; - content.Add($"Node '{node.GetName()}' responded with {jsonInsert}"); - } - catch (Exception ex) - { - content.Add($"Node '{node.GetName()}' failed to respond with exception: " + ex); - } + var info = node.GetDebugInfo(); + var json = JsonConvert.SerializeObject(info, Formatting.Indented); + var jsonLines = json.Split(Environment.NewLine); + content.Add($"Node '{node.GetName()}' responded with:"); + content.Add("---"); + content.AddRange(jsonLines); + content.Add("---"); } + catch (Exception ex) + { + content.Add($"Node '{node.GetName()}' failed to respond with exception: " + ex); + } + } - await context.Followup(string.Join(nl, content)); - }); + return content.ToArray(); } } @@ -326,33 +342,41 @@ namespace BiblioTech.Commands var peerId = await peerIdOption.Parse(context); if (string.IsNullOrEmpty(peerId)) return; - await OnDeployment(context, async (group, name) => + var report = await OnDeployment(context, (group, name) => CreateDebugPeerReport(group, name, peerId)); + if (report != null && report.Any()) { - var nl = Environment.NewLine; - var content = new List - { - $"{DateTime.UtcNow.ToString("o")} - {group.Count()} Codex nodes.", - $"Deployment name: '{name}'" - }; + await context.Followup(report); + } + } - content.Add($"Calling debug/peer for '{peerId}' on {group.Count()} Codex nodes."); - foreach (var node in group) + private string[] CreateDebugPeerReport(ICodexNodeGroup group, string name, string peerId) + { + var content = new List + { + $"{DateTime.UtcNow.ToString("o")} - {group.Count()} Codex nodes.", + $"Deployment name: '{name}'.", + $"Calling debug/peer for '{peerId}'." + }; + + foreach (var node in group) + { + try { - try - { - var info = node.GetDebugPeer(peerId); - var json = JsonConvert.SerializeObject(info, Formatting.Indented); - var jsonInsert = $"{nl}```{nl}{json}{nl}```{nl}"; - content.Add($"Node '{node.GetName()}' responded with {jsonInsert}"); - } - catch (Exception ex) - { - content.Add($"Node '{node.GetName()}' failed to respond with exception: " + ex); - } + var info = node.GetDebugPeer(peerId); + var json = JsonConvert.SerializeObject(info, Formatting.Indented); + var jsonLines = json.Split(Environment.NewLine); + content.Add($"Node '{node.GetName()}' responded with:"); + content.Add("---"); + content.AddRange(jsonLines); + content.Add("---"); } + catch (Exception ex) + { + content.Add($"Node '{node.GetName()}' failed to respond with exception: " + ex); + } + } - await context.Followup(string.Join(nl, content)); - }); + return content.ToArray(); } } } diff --git a/Tools/BiblioTech/Options/CommandContext.cs b/Tools/BiblioTech/Options/CommandContext.cs index d3293e1..42a065a 100644 --- a/Tools/BiblioTech/Options/CommandContext.cs +++ b/Tools/BiblioTech/Options/CommandContext.cs @@ -4,36 +4,99 @@ namespace BiblioTech.Options { public class CommandContext { + private const string AttachmentFolder = "attachments"; + public CommandContext(SocketSlashCommand command, IReadOnlyCollection options) { Command = command; Options = options; + + var attachmentPath = Path.Combine(Program.Config.DataPath, AttachmentFolder); + if (Directory.Exists(attachmentPath)) + { + Directory.CreateDirectory(attachmentPath); + } } public SocketSlashCommand Command { get; } public IReadOnlyCollection Options { get; } - public async Task Followup(string message) + public async Task Followup(string line) { - if (message.Length > 1900) - { - await FollowupWithAttachement("codexdiscordbot_file.txt", message); - return; - } + var array = line.Split(Environment.NewLine); + await Followup(array); + } + + public async Task Followup(string[] lines) + { + var chunker = new LineChunker(lines); + var chunks = chunker.GetChunks(); + if (!chunks.Any()) return; + + // First chunk is a modification of the original message. + // Everything after that, we must create a new message. + var first = chunks.First(); + chunks.RemoveAt(0); await Command.ModifyOriginalResponseAsync(m => { - m.Content = message; + m.Content = FormatChunk(first); }); + + foreach (var remaining in chunks) + { + await Command.FollowupAsync(FormatChunk(remaining)); + } } - private async Task FollowupWithAttachement(string filename, string content) + private string FormatChunk(string[] chunk) { - using var fileStream = new MemoryStream(); - using var streamWriter = new StreamWriter(fileStream); - await streamWriter.WriteAsync(content); + return string.Join(Environment.NewLine, chunk); + } + } - await Command.FollowupWithFileAsync(fileStream, filename); + public class LineChunker + { + private readonly List input; + private readonly int maxCharacters; + + public LineChunker(string[] input, int maxCharacters = 1950) + { + this.input = input.ToList(); + this.maxCharacters = maxCharacters; + } + + public List GetChunks() + { + var result = new List(); + while (input.Any()) + { + result.Add(GetChunk()); + } + + return result; + } + + private string[] GetChunk() + { + var totalLength = 0; + var result = new List(); + + while (input.Any()) + { + var nextLine = input[0]; + var nextLength = totalLength + nextLine.Length; + if (nextLength > maxCharacters) + { + return result.ToArray(); + } + + input.RemoveAt(0); + result.Add(nextLine); + totalLength += nextLine.Length; + } + + return result.ToArray(); } } } diff --git a/Tools/BiblioTech/UserRepo.cs b/Tools/BiblioTech/UserRepo.cs index 1a78ac4..0db6b6f 100644 --- a/Tools/BiblioTech/UserRepo.cs +++ b/Tools/BiblioTech/UserRepo.cs @@ -76,17 +76,17 @@ namespace BiblioTech return result.ToArray(); } - public string GetUserReport(IUser user) + public string[] GetUserReport(IUser user) { var userData = GetUserData(user); - if (userData == null) return "User has not joined the test net."; + if (userData == null) return new[] { "User has not joined the test net." }; return userData.CreateOverview(); } - public string GetUserReport(EthAddress ethAddress) + public string[] GetUserReport(EthAddress ethAddress) { var userData = GetUserDataForAddress(ethAddress); - if (userData == null) return "No user is using this eth address."; + if (userData == null) return new[] { "No user is using this eth address." }; return userData.CreateOverview(); } @@ -196,14 +196,15 @@ namespace BiblioTech public List AssociateEvents { get; } public List MintEvents { get; } - public string CreateOverview() + public string[] CreateOverview() { - var nl = Environment.NewLine; - return - $"name: '{Name}' - id:{DiscordId}{nl}" + - $"joined: {CreatedUtc.ToString("o")}{nl}" + - $"current address: {CurrentAddress}{nl}" + - $"{AssociateEvents.Count + MintEvents.Count} total bot events."; + return new[] + { + $"name: '{Name}' - id:{DiscordId}", + $"joined: {CreatedUtc.ToString("o")}", + $"current address: {CurrentAddress}", + $"{AssociateEvents.Count + MintEvents.Count} total bot events." + }; } } From 1ed93bbdde63c566753ba8ee9b7d0a73dd64bf3a Mon Sep 17 00:00:00 2001 From: benbierens Date: Thu, 9 Nov 2023 10:37:57 +0100 Subject: [PATCH 06/25] Adds required admin permission for admin commands. --- Tools/BiblioTech/BaseCommand.cs | 9 ++------- Tools/BiblioTech/CommandHandler.cs | 5 +++++ Tools/BiblioTech/Commands/AdminCommand.cs | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Tools/BiblioTech/BaseCommand.cs b/Tools/BiblioTech/BaseCommand.cs index 45f6ea6..ce618f9 100644 --- a/Tools/BiblioTech/BaseCommand.cs +++ b/Tools/BiblioTech/BaseCommand.cs @@ -9,13 +9,8 @@ namespace BiblioTech public abstract string Name { get; } public abstract string StartingMessage { get; } public abstract string Description { get; } - public virtual CommandOption[] Options - { - get - { - return Array.Empty(); - } - } + public virtual CommandOption[] Options => Array.Empty(); + public virtual bool IsAdminCommand => false; public async Task SlashCommandHandler(SocketSlashCommand command) { diff --git a/Tools/BiblioTech/CommandHandler.cs b/Tools/BiblioTech/CommandHandler.cs index 88b45aa..29d5b85 100644 --- a/Tools/BiblioTech/CommandHandler.cs +++ b/Tools/BiblioTech/CommandHandler.cs @@ -30,6 +30,11 @@ namespace BiblioTech .WithName(c.Name) .WithDescription(c.Description); + if (c.IsAdminCommand) + { + builder.WithDefaultMemberPermissions(GuildPermission.Administrator); + } + foreach (var option in c.Options) { builder.AddOption(option.Build()); diff --git a/Tools/BiblioTech/Commands/AdminCommand.cs b/Tools/BiblioTech/Commands/AdminCommand.cs index 9def408..47a161a 100644 --- a/Tools/BiblioTech/Commands/AdminCommand.cs +++ b/Tools/BiblioTech/Commands/AdminCommand.cs @@ -25,6 +25,7 @@ namespace BiblioTech.Commands public override string Name => "admin"; public override string StartingMessage => "..."; public override string Description => "Admins only."; + public override bool IsAdminCommand => true; public override CommandOption[] Options => new CommandOption[] { From 26ec841db1ab102574528fc0d6d6b5b880a8a389 Mon Sep 17 00:00:00 2001 From: benbierens Date: Thu, 9 Nov 2023 10:45:28 +0100 Subject: [PATCH 07/25] locks out default permissions for admin commands --- Tools/BiblioTech/CommandHandler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Tools/BiblioTech/CommandHandler.cs b/Tools/BiblioTech/CommandHandler.cs index 29d5b85..433f657 100644 --- a/Tools/BiblioTech/CommandHandler.cs +++ b/Tools/BiblioTech/CommandHandler.cs @@ -32,6 +32,7 @@ namespace BiblioTech if (c.IsAdminCommand) { + builder.WithDefaultPermission(false); builder.WithDefaultMemberPermissions(GuildPermission.Administrator); } From 84382b4ab4136c6ef5efd3e8a4ea2bd75d1ae9ae Mon Sep 17 00:00:00 2001 From: benbierens Date: Thu, 9 Nov 2023 11:14:17 +0100 Subject: [PATCH 08/25] Removes server-admin requirement for admin commands --- Tools/BiblioTech/BaseCommand.cs | 1 - Tools/BiblioTech/CommandHandler.cs | 6 ------ Tools/BiblioTech/Commands/AdminCommand.cs | 1 - 3 files changed, 8 deletions(-) diff --git a/Tools/BiblioTech/BaseCommand.cs b/Tools/BiblioTech/BaseCommand.cs index ce618f9..dc23d68 100644 --- a/Tools/BiblioTech/BaseCommand.cs +++ b/Tools/BiblioTech/BaseCommand.cs @@ -10,7 +10,6 @@ namespace BiblioTech public abstract string StartingMessage { get; } public abstract string Description { get; } public virtual CommandOption[] Options => Array.Empty(); - public virtual bool IsAdminCommand => false; public async Task SlashCommandHandler(SocketSlashCommand command) { diff --git a/Tools/BiblioTech/CommandHandler.cs b/Tools/BiblioTech/CommandHandler.cs index 433f657..88b45aa 100644 --- a/Tools/BiblioTech/CommandHandler.cs +++ b/Tools/BiblioTech/CommandHandler.cs @@ -30,12 +30,6 @@ namespace BiblioTech .WithName(c.Name) .WithDescription(c.Description); - if (c.IsAdminCommand) - { - builder.WithDefaultPermission(false); - builder.WithDefaultMemberPermissions(GuildPermission.Administrator); - } - foreach (var option in c.Options) { builder.AddOption(option.Build()); diff --git a/Tools/BiblioTech/Commands/AdminCommand.cs b/Tools/BiblioTech/Commands/AdminCommand.cs index 47a161a..9def408 100644 --- a/Tools/BiblioTech/Commands/AdminCommand.cs +++ b/Tools/BiblioTech/Commands/AdminCommand.cs @@ -25,7 +25,6 @@ namespace BiblioTech.Commands public override string Name => "admin"; public override string StartingMessage => "..."; public override string Description => "Admins only."; - public override bool IsAdminCommand => true; public override CommandOption[] Options => new CommandOption[] { From 5a7608460bc105166a92ea11dc0926b3e008a5c4 Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 13 Nov 2023 13:58:34 +0100 Subject: [PATCH 09/25] retry by setting nodeport explicitly --- Framework/KubernetesWorkflow/K8sController.cs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/Framework/KubernetesWorkflow/K8sController.cs b/Framework/KubernetesWorkflow/K8sController.cs index 4d4ca11..e0ca025 100644 --- a/Framework/KubernetesWorkflow/K8sController.cs +++ b/Framework/KubernetesWorkflow/K8sController.cs @@ -656,17 +656,17 @@ namespace KubernetesWorkflow private RunningService? CreateInternalService(ContainerRecipe[] recipes) { - return CreateService(recipes, r => r.InternalPorts.Concat(r.ExposedPorts).ToArray(), "ClusterIP", "int"); + return CreateService(recipes, r => r.InternalPorts.Concat(r.ExposedPorts).ToArray(), "ClusterIP", "int", false); } private RunningService? CreateExternalService(ContainerRecipe[] recipes) { - return CreateService(recipes, r => r.ExposedPorts, "NodePort", "ext"); + return CreateService(recipes, r => r.ExposedPorts, "NodePort", "ext", true); } - private RunningService? CreateService(ContainerRecipe[] recipes, Func portSelector, string serviceType, string namePostfix) + private RunningService? CreateService(ContainerRecipe[] recipes, Func portSelector, string serviceType, string namePostfix, bool isNodePort) { - var ports = CreateServicePorts(recipes, portSelector); + var ports = CreateServicePorts(recipes, portSelector, isNodePort); if (!ports.Any()) return null; var serviceSpec = new V1Service @@ -740,7 +740,7 @@ namespace KubernetesWorkflow }; } - private List CreateServicePorts(ContainerRecipe[] recipes, Func portSelector) + private List CreateServicePorts(ContainerRecipe[] recipes, Func portSelector, bool isNodePort) { var result = new List(); foreach (var recipe in recipes) @@ -748,29 +748,33 @@ namespace KubernetesWorkflow var ports = portSelector(recipe); foreach (var port in ports) { - result.AddRange(CreateServicePorts(recipe, port)); + result.AddRange(CreateServicePorts(recipe, port, isNodePort)); } } return result; } - private List CreateServicePorts(ContainerRecipe recipe, Port recipePort) + private List CreateServicePorts(ContainerRecipe recipe, Port recipePort, bool isNodePort) { var result = new List(); - if (recipePort.IsTcp()) CreateServicePort(result, recipe, recipePort, "TCP"); - if (recipePort.IsUdp()) CreateServicePort(result, recipe, recipePort, "UDP"); + if (recipePort.IsTcp()) CreateServicePort(result, recipe, recipePort, "TCP", isNodePort); + if (recipePort.IsUdp()) CreateServicePort(result, recipe, recipePort, "UDP", isNodePort); return result; } - private void CreateServicePort(List result, ContainerRecipe recipe, Port port, string protocol) + private void CreateServicePort(List result, ContainerRecipe recipe, Port port, string protocol, bool isNodePort) { - result.Add(new V1ServicePort + var p = new V1ServicePort { Name = GetNameForPort(recipe, port), Protocol = protocol, Port = port.Number, TargetPort = GetNameForPort(recipe, port) - }); + }; + + if (isNodePort) p.NodePort = port.Number; + + result.Add(p); } #endregion From ca350604e35614637000ad0079d8a406fc879961 Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 13 Nov 2023 15:11:49 +0100 Subject: [PATCH 10/25] Debugging nodeport --- Framework/KubernetesWorkflow/StartupWorkflow.cs | 4 ++-- Framework/Logging/BaseLog.cs | 4 +++- Framework/Logging/NullLog.cs | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Framework/KubernetesWorkflow/StartupWorkflow.cs b/Framework/KubernetesWorkflow/StartupWorkflow.cs index 20ab753..49bc764 100644 --- a/Framework/KubernetesWorkflow/StartupWorkflow.cs +++ b/Framework/KubernetesWorkflow/StartupWorkflow.cs @@ -222,7 +222,7 @@ namespace KubernetesWorkflow } catch (k8s.Autorest.HttpOperationException ex) { - log.Error(JsonConvert.SerializeObject(ex)); + log.Error(JsonConvert.SerializeObject(ex.Response)); throw; } } @@ -238,7 +238,7 @@ namespace KubernetesWorkflow } catch (k8s.Autorest.HttpOperationException ex) { - log.Error(JsonConvert.SerializeObject(ex)); + log.Error(JsonConvert.SerializeObject(ex.Response)); throw; } } diff --git a/Framework/Logging/BaseLog.cs b/Framework/Logging/BaseLog.cs index bb5df83..cc323a8 100644 --- a/Framework/Logging/BaseLog.cs +++ b/Framework/Logging/BaseLog.cs @@ -55,7 +55,9 @@ namespace Logging public virtual void Error(string message) { - Log($"[ERROR] {message}"); + var msg = $"[ERROR] {message}"; + Console.WriteLine(msg); + Log(msg); } public virtual void AddStringReplace(string from, string to) diff --git a/Framework/Logging/NullLog.cs b/Framework/Logging/NullLog.cs index fb7a70e..a9cf8d2 100644 --- a/Framework/Logging/NullLog.cs +++ b/Framework/Logging/NullLog.cs @@ -16,6 +16,7 @@ public override void Error(string message) { + Console.WriteLine("Error: " + message); base.Error(message); } From b82c74865ea18435bce84a1a54b34886b0910860 Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 13 Nov 2023 15:27:52 +0100 Subject: [PATCH 11/25] Adds numberSource for correct range for external ports --- .../Recipe/ContainerRecipeFactory.cs | 4 ++-- .../Recipe/RecipeComponentFactory.cs | 12 +++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Framework/KubernetesWorkflow/Recipe/ContainerRecipeFactory.cs b/Framework/KubernetesWorkflow/Recipe/ContainerRecipeFactory.cs index 134b280..c10d4a1 100644 --- a/Framework/KubernetesWorkflow/Recipe/ContainerRecipeFactory.cs +++ b/Framework/KubernetesWorkflow/Recipe/ContainerRecipeFactory.cs @@ -52,7 +52,7 @@ namespace KubernetesWorkflow.Recipe protected Port AddExposedPort(string tag, PortProtocol protocol = PortProtocol.TCP) { - return AddExposedPort(factory.CreatePort(tag, protocol)); + return AddExposedPort(factory.CreateExternalPort(tag, protocol)); } protected Port AddExposedPort(int number, string tag, PortProtocol protocol = PortProtocol.TCP) @@ -62,7 +62,7 @@ namespace KubernetesWorkflow.Recipe protected Port AddInternalPort(string tag = "", PortProtocol protocol = PortProtocol.TCP) { - var p = factory.CreatePort(tag, protocol); + var p = factory.CreateInternalPort(tag, protocol); internalPorts.Add(p); return p; } diff --git a/Framework/KubernetesWorkflow/Recipe/RecipeComponentFactory.cs b/Framework/KubernetesWorkflow/Recipe/RecipeComponentFactory.cs index c144746..b881d47 100644 --- a/Framework/KubernetesWorkflow/Recipe/RecipeComponentFactory.cs +++ b/Framework/KubernetesWorkflow/Recipe/RecipeComponentFactory.cs @@ -5,16 +5,22 @@ namespace KubernetesWorkflow.Recipe { public class RecipeComponentFactory { - private NumberSource portNumberSource = new NumberSource(8080); + private NumberSource internalNumberSource = new NumberSource(8080); + private NumberSource externalNumberSource = new NumberSource(30000); public Port CreatePort(int number, string tag, PortProtocol protocol) { return new Port(number, tag, protocol); } - public Port CreatePort(string tag, PortProtocol protocol) + public Port CreateInternalPort(string tag, PortProtocol protocol) { - return new Port(portNumberSource.GetNextNumber(), tag, protocol); + return new Port(internalNumberSource.GetNextNumber(), tag, protocol); + } + + public Port CreateExternalPort(string tag, PortProtocol protocol) + { + return new Port(externalNumberSource.GetNextNumber(), tag, protocol); } public EnvVar CreateEnvVar(string name, int value) From 4192952a3747dd4a59175e9090e0bab5241d0095 Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 13 Nov 2023 16:05:41 +0100 Subject: [PATCH 12/25] Prevents reuse of external port numbers --- Framework/KubernetesWorkflow/Recipe/RecipeComponentFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Framework/KubernetesWorkflow/Recipe/RecipeComponentFactory.cs b/Framework/KubernetesWorkflow/Recipe/RecipeComponentFactory.cs index b881d47..8b4ef7f 100644 --- a/Framework/KubernetesWorkflow/Recipe/RecipeComponentFactory.cs +++ b/Framework/KubernetesWorkflow/Recipe/RecipeComponentFactory.cs @@ -6,7 +6,7 @@ namespace KubernetesWorkflow.Recipe public class RecipeComponentFactory { private NumberSource internalNumberSource = new NumberSource(8080); - private NumberSource externalNumberSource = new NumberSource(30000); + private static NumberSource externalNumberSource = new NumberSource(30000); public Port CreatePort(int number, string tag, PortProtocol protocol) { From b47b5960629379268c8996a53f1981d5ed222912 Mon Sep 17 00:00:00 2001 From: benbierens Date: Tue, 14 Nov 2023 10:49:14 +0100 Subject: [PATCH 13/25] Fetches used external ports in order to guarantee no collisions. --- Framework/KubernetesWorkflow/K8sController.cs | 25 +++++++++++++++++++ .../Recipe/ContainerRecipeFactory.cs | 2 +- .../Recipe/RecipeComponentFactory.cs | 24 ++++++++++++++---- .../KubernetesWorkflow/StartupWorkflow.cs | 7 ++++++ 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/Framework/KubernetesWorkflow/K8sController.cs b/Framework/KubernetesWorkflow/K8sController.cs index e0ca025..5b657d2 100644 --- a/Framework/KubernetesWorkflow/K8sController.cs +++ b/Framework/KubernetesWorkflow/K8sController.cs @@ -84,6 +84,31 @@ namespace KubernetesWorkflow return result; } + public int[] GetUsedExternalPorts() + { + return client.Run(c => + { + var result = new List(); + + var services = c.ListServiceForAllNamespaces(); + var nodePorts = services.Items.Where(s => s.Spec.Type == "NodePort").ToArray(); + if (!nodePorts.Any()) return result.ToArray(); + + foreach (var service in nodePorts) + { + foreach (var port in service.Spec.Ports) + { + if (port.NodePort.HasValue) + { + result.Add(port.NodePort.Value); + } + } + } + + return result.ToArray(); + }); + } + public void DeleteAllNamespacesStartingWith(string prefix) { log.Debug(); diff --git a/Framework/KubernetesWorkflow/Recipe/ContainerRecipeFactory.cs b/Framework/KubernetesWorkflow/Recipe/ContainerRecipeFactory.cs index c10d4a1..0a988de 100644 --- a/Framework/KubernetesWorkflow/Recipe/ContainerRecipeFactory.cs +++ b/Framework/KubernetesWorkflow/Recipe/ContainerRecipeFactory.cs @@ -57,7 +57,7 @@ namespace KubernetesWorkflow.Recipe protected Port AddExposedPort(int number, string tag, PortProtocol protocol = PortProtocol.TCP) { - return AddExposedPort(factory.CreatePort(number, tag, protocol)); + return AddExposedPort(factory.CreateExternalPort(number, tag, protocol)); } protected Port AddInternalPort(string tag = "", PortProtocol protocol = PortProtocol.TCP) diff --git a/Framework/KubernetesWorkflow/Recipe/RecipeComponentFactory.cs b/Framework/KubernetesWorkflow/Recipe/RecipeComponentFactory.cs index 8b4ef7f..3935385 100644 --- a/Framework/KubernetesWorkflow/Recipe/RecipeComponentFactory.cs +++ b/Framework/KubernetesWorkflow/Recipe/RecipeComponentFactory.cs @@ -5,12 +5,13 @@ namespace KubernetesWorkflow.Recipe { public class RecipeComponentFactory { - private NumberSource internalNumberSource = new NumberSource(8080); - private static NumberSource externalNumberSource = new NumberSource(30000); + private readonly NumberSource internalNumberSource = new NumberSource(8080); + private static readonly NumberSource externalNumberSource = new NumberSource(30000); + private static int[] usedExternalPorts = Array.Empty(); - public Port CreatePort(int number, string tag, PortProtocol protocol) + public void Update(K8sController controller) { - return new Port(number, tag, protocol); + usedExternalPorts = controller.GetUsedExternalPorts(); } public Port CreateInternalPort(string tag, PortProtocol protocol) @@ -18,9 +19,22 @@ namespace KubernetesWorkflow.Recipe return new Port(internalNumberSource.GetNextNumber(), tag, protocol); } + public Port CreateExternalPort(int number, string tag, PortProtocol protocol) + { + if (usedExternalPorts.Contains(number)) throw new Exception($"External port number {number} is already in use by the cluster."); + return new Port(number, tag, protocol); + } + public Port CreateExternalPort(string tag, PortProtocol protocol) { - return new Port(externalNumberSource.GetNextNumber(), tag, protocol); + while (true) + { + var number = externalNumberSource.GetNextNumber(); + if (!usedExternalPorts.Contains(number)) + { + return new Port(number, tag, protocol); + } + } } public EnvVar CreateEnvVar(string name, int value) diff --git a/Framework/KubernetesWorkflow/StartupWorkflow.cs b/Framework/KubernetesWorkflow/StartupWorkflow.cs index 49bc764..15bc3e3 100644 --- a/Framework/KubernetesWorkflow/StartupWorkflow.cs +++ b/Framework/KubernetesWorkflow/StartupWorkflow.cs @@ -54,12 +54,19 @@ namespace KubernetesWorkflow { return K8s(controller => { + componentFactory.Update(controller); + var recipes = CreateRecipes(numberOfContainers, recipeFactory, startupConfig); var startResult = controller.BringOnline(recipes, location); var containers = CreateContainers(startResult, recipes, startupConfig); var rc = new RunningContainers(startupConfig, startResult, containers); cluster.Configuration.Hooks.OnContainersStarted(rc); + + if (startResult.ExternalService != null) + { + componentFactory.Update(controller); + } return rc; }); } From b90c47a994f0fba8acc0efdbe0758f98f27b1992 Mon Sep 17 00:00:00 2001 From: benbierens Date: Wed, 15 Nov 2023 14:53:25 +0100 Subject: [PATCH 14/25] Switches to automatic public IP address for codex and geth containers. --- ProjectPlugins/CodexPlugin/CodexApiTypes.cs | 3 ++- .../CodexPlugin/CodexContainerRecipe.cs | 4 +++- ProjectPlugins/CodexPlugin/CodexSetup.cs | 2 +- .../CodexPlugin/CodexStartupConfig.cs | 1 - .../GethPlugin/GethContainerRecipe.cs | 17 +++++++++-------- ProjectPlugins/GethPlugin/GethStartupConfig.cs | 4 +--- Tools/CodexNetDeployer/CodexNodeStarter.cs | 1 - Tools/CodexNetDeployer/Configuration.cs | 7 ------- Tools/CodexNetDeployer/Deployer.cs | 1 - Tools/CodexNetDeployer/deploy-public-testnet.sh | 2 -- 10 files changed, 16 insertions(+), 26 deletions(-) diff --git a/ProjectPlugins/CodexPlugin/CodexApiTypes.cs b/ProjectPlugins/CodexPlugin/CodexApiTypes.cs index af52383..b1965cd 100644 --- a/ProjectPlugins/CodexPlugin/CodexApiTypes.cs +++ b/ProjectPlugins/CodexPlugin/CodexApiTypes.cs @@ -5,9 +5,10 @@ namespace CodexPlugin public class CodexDebugResponse { public string id { get; set; } = string.Empty; - public string[] addrs { get; set; } = new string[0]; + public string[] addrs { get; set; } = Array.Empty(); public string repo { get; set; } = string.Empty; public string spr { get; set; } = string.Empty; + public string[] announceAddresses { get; set; } = Array.Empty(); public EnginePeerResponse[] enginePeers { get; set; } = Array.Empty(); public SwitchPeerResponse[] switchPeers { get; set; } = Array.Empty(); public CodexDebugVersionResponse codex { get; set; } = new(); diff --git a/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs b/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs index 744ae37..d08675a 100644 --- a/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs +++ b/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs @@ -47,13 +47,15 @@ namespace CodexPlugin if (config.PublicTestNet != null) { - AddEnvVar("CODEX_NAT", config.PublicTestNet.PublicNatIP); + // This makes the node announce itself to its public IP address. AddEnvVar("NAT_IP_AUTO", "false"); + AddEnvVar("NAT_PUBLIC_IP_AUTO", "true"); } else { // This makes the node announce itself to its local (pod) IP address. AddEnvVar("NAT_IP_AUTO", "true"); + AddEnvVar("NAT_PUBLIC_IP_AUTO", "false"); } var listenPort = CreateListenPort(config); diff --git a/ProjectPlugins/CodexPlugin/CodexSetup.cs b/ProjectPlugins/CodexPlugin/CodexSetup.cs index 8b7dbb1..a491fb3 100644 --- a/ProjectPlugins/CodexPlugin/CodexSetup.cs +++ b/ProjectPlugins/CodexPlugin/CodexSetup.cs @@ -133,7 +133,7 @@ namespace CodexPlugin private IEnumerable DescribeArgs() { - if (PublicTestNet != null) yield return $"Public TestNet at {PublicTestNet.PublicNatIP}:{PublicTestNet.PublicListenPort}"; + if (PublicTestNet != null) yield return $"Public TestNet with listenPort: {PublicTestNet.PublicListenPort}"; yield return $"LogLevel={LogLevelWithTopics()}"; if (BootstrapSpr != null) yield return $"BootstrapNode={BootstrapSpr}"; if (StorageQuota != null) yield return $"StorageQuota={StorageQuota}"; diff --git a/ProjectPlugins/CodexPlugin/CodexStartupConfig.cs b/ProjectPlugins/CodexPlugin/CodexStartupConfig.cs index 6e59f2a..ed840d0 100644 --- a/ProjectPlugins/CodexPlugin/CodexStartupConfig.cs +++ b/ProjectPlugins/CodexPlugin/CodexStartupConfig.cs @@ -65,7 +65,6 @@ namespace CodexPlugin public class CodexTestNetConfig { - public string PublicNatIP { get; set; } = string.Empty; public int PublicDiscoveryPort { get; set; } public int PublicListenPort { get; set; } } diff --git a/ProjectPlugins/GethPlugin/GethContainerRecipe.cs b/ProjectPlugins/GethPlugin/GethContainerRecipe.cs index 35b994c..beda54c 100644 --- a/ProjectPlugins/GethPlugin/GethContainerRecipe.cs +++ b/ProjectPlugins/GethPlugin/GethContainerRecipe.cs @@ -40,7 +40,7 @@ namespace GethPlugin var authRpc = CreateP2pPort(config, tag: AuthRpcPortTag); var wsPort = CreateP2pPort(config, tag: WsPortTag); - var args = $"--http.addr 0.0.0.0 --http.port {httpPort.Number} --port {listen.Number} --discovery.port {discovery.Number} {GetTestNetArgs(config)} {defaultArgs}"; + var args = $"--http.addr 0.0.0.0 --http.port {httpPort.Number} --port {listen.Number} --discovery.port {discovery.Number} {defaultArgs}"; if (config.BootstrapNode != null) { @@ -50,6 +50,14 @@ namespace GethPlugin var bootstrapArg = $" --bootnodes enode://{bootPubKey}@{bootIp}:{bootPort} --nat=extip:{bootIp}"; args += bootstrapArg; } + if (config.IsPublicTestNet != null) + { + AddEnvVar("NAT_PUBLIC_IP_AUTO", "true"); + } + else + { + AddEnvVar("NAT_PUBLIC_IP_AUTO", "false"); + } return args + $" --authrpc.port {authRpc.Number} --ws --ws.addr 0.0.0.0 --ws.port {wsPort.Number}"; } @@ -64,13 +72,6 @@ namespace GethPlugin AddEnvVar("UNLOCK_NUMBER", numberOfAccounts.ToString()); } - private string GetTestNetArgs(GethStartupConfig config) - { - if (config.IsPublicTestNet == null) return string.Empty; - - return $"--nat=extip:{config.IsPublicTestNet.PublicIp}"; - } - private Port CreateDiscoveryPort(GethStartupConfig config) { if (config.IsPublicTestNet == null) return AddInternalPort(DiscoveryPortTag); diff --git a/ProjectPlugins/GethPlugin/GethStartupConfig.cs b/ProjectPlugins/GethPlugin/GethStartupConfig.cs index 0776541..0d6b86e 100644 --- a/ProjectPlugins/GethPlugin/GethStartupConfig.cs +++ b/ProjectPlugins/GethPlugin/GethStartupConfig.cs @@ -42,14 +42,12 @@ public class GethTestNetConfig { - public GethTestNetConfig(string publicIp, int discoveryPort, int listenPort) + public GethTestNetConfig(int discoveryPort, int listenPort) { - PublicIp = publicIp; DiscoveryPort = discoveryPort; ListenPort = listenPort; } - public string PublicIp { get; } public int DiscoveryPort { get; } public int ListenPort { get; } } diff --git a/Tools/CodexNetDeployer/CodexNodeStarter.cs b/Tools/CodexNetDeployer/CodexNodeStarter.cs index e18720e..7b54001 100644 --- a/Tools/CodexNetDeployer/CodexNodeStarter.cs +++ b/Tools/CodexNetDeployer/CodexNodeStarter.cs @@ -105,7 +105,6 @@ namespace CodexNetDeployer return new CodexTestNetConfig { - PublicNatIP = config.PublicIP, PublicDiscoveryPort = Convert.ToInt32(discPort), PublicListenPort = Convert.ToInt32(listenPort) }; diff --git a/Tools/CodexNetDeployer/Configuration.cs b/Tools/CodexNetDeployer/Configuration.cs index 8279f96..da654fa 100644 --- a/Tools/CodexNetDeployer/Configuration.cs +++ b/Tools/CodexNetDeployer/Configuration.cs @@ -86,18 +86,12 @@ namespace CodexNetDeployer [Uniform("public-testnet", "ptn", "PUBLICTESTNET", false, "If true, deployment is created for public exposure. Default is false.")] public bool IsPublicTestNet { get; set; } = false; - [Uniform("public-ip", "pip", "PUBLICIP", false, "Required if public-testnet is true. Public IP address used by nodes for network annoucements.")] - public string PublicIP { get; set; } = string.Empty; - [Uniform("public-discports", "pdps", "PUBLICDISCPORTS", false, "Required if public-testnet is true. Comma-separated port numbers used for discovery. Number must match number of nodes.")] public string PublicDiscPorts { get; set; } = string.Empty; [Uniform("public-listenports", "plps", "PUBLICLISTENPORTS", false, "Required if public-testnet is true. Comma-separated port numbers used for listening. Number must match number of nodes.")] public string PublicListenPorts { get; set; } = string.Empty; - [Uniform("public-gethip", "pgdp", "PUBLICGETHIP", false, "Required if public-testnet is true. Geth's public IP address.")] - public string PublicGethIP { get; set; } = string.Empty; - [Uniform("public-gethdiscport", "pgdp", "PUBLICGETHDISCPORT", false, "Required if public-testnet is true. Single port number used for Geth's public discovery port.")] public int PublicGethDiscPort { get; set; } @@ -150,7 +144,6 @@ namespace CodexNetDeployer if (IsPublicTestNet) { - if (string.IsNullOrEmpty(PublicIP)) errors.Add("Public IP required when deploying public testnet."); if (PublicDiscPorts.Split(",").Length != NumberOfCodexNodes) errors.Add("Number of public discovery-ports provided does not match number of codex nodes."); if (PublicListenPorts.Split(",").Length != NumberOfCodexNodes) errors.Add("Number of public listen-ports provided does not match number of codex nodes."); if (PublicGethDiscPort == 0) errors.Add("Geth public discovery port is not set."); diff --git a/Tools/CodexNetDeployer/Deployer.cs b/Tools/CodexNetDeployer/Deployer.cs index a0cb0ad..30316bd 100644 --- a/Tools/CodexNetDeployer/Deployer.cs +++ b/Tools/CodexNetDeployer/Deployer.cs @@ -113,7 +113,6 @@ namespace CodexNetDeployer if (config.IsPublicTestNet) { s.AsPublicTestNet(new GethTestNetConfig( - publicIp: config.PublicGethIP, discoveryPort: config.PublicGethDiscPort, listenPort: config.PublicGethListenPort )); diff --git a/Tools/CodexNetDeployer/deploy-public-testnet.sh b/Tools/CodexNetDeployer/deploy-public-testnet.sh index 159315a..c25d010 100644 --- a/Tools/CodexNetDeployer/deploy-public-testnet.sh +++ b/Tools/CodexNetDeployer/deploy-public-testnet.sh @@ -16,10 +16,8 @@ dotnet run \ --check-connect=0 \ \ --public-testnet=1 \ - --public-ip=1.2.3.4 \ --public-discports=30010,30020,30030 \ --public-listenports=30011,30021,30031 \ - --public-gethip=1.2.3.5 \ --public-gethdiscport=30040 \ --public-gethlistenport=30041 \ \ From 8b224f69221e5bc9928855cf6e82f1ff30324fba Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 20 Nov 2023 11:19:10 +0100 Subject: [PATCH 15/25] Updates public ip to point to service --- ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs b/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs index d08675a..5907f96 100644 --- a/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs +++ b/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs @@ -49,13 +49,12 @@ namespace CodexPlugin { // This makes the node announce itself to its public IP address. AddEnvVar("NAT_IP_AUTO", "false"); - AddEnvVar("NAT_PUBLIC_IP_AUTO", "true"); + AddEnvVar("NAT_PUBLIC_IP_AUTO", "https://ipinfo.io/ip"); } else { // This makes the node announce itself to its local (pod) IP address. AddEnvVar("NAT_IP_AUTO", "true"); - AddEnvVar("NAT_PUBLIC_IP_AUTO", "false"); } var listenPort = CreateListenPort(config); From 54471d41d59834f2f781dc6073f6d334793e6222 Mon Sep 17 00:00:00 2001 From: benbierens Date: Tue, 21 Nov 2023 09:03:52 +0100 Subject: [PATCH 16/25] Allows for deployment without codex nodes --- ProjectPlugins/MetricsPlugin/PrometheusStarter.cs | 2 ++ Tools/CodexNetDeployer/Deployer.cs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ProjectPlugins/MetricsPlugin/PrometheusStarter.cs b/ProjectPlugins/MetricsPlugin/PrometheusStarter.cs index 0f0bc13..53f784e 100644 --- a/ProjectPlugins/MetricsPlugin/PrometheusStarter.cs +++ b/ProjectPlugins/MetricsPlugin/PrometheusStarter.cs @@ -18,6 +18,8 @@ namespace MetricsPlugin public RunningContainers CollectMetricsFor(IMetricsScrapeTarget[] targets) { + if (!targets.Any()) throw new ArgumentException(nameof(targets) + " must not be empty."); + Log($"Starting metrics server for {targets.Length} targets..."); var startupConfig = new StartupConfig(); startupConfig.Add(new PrometheusStartupConfig(GeneratePrometheusConfig(targets))); diff --git a/Tools/CodexNetDeployer/Deployer.cs b/Tools/CodexNetDeployer/Deployer.cs index 30316bd..bf993ef 100644 --- a/Tools/CodexNetDeployer/Deployer.cs +++ b/Tools/CodexNetDeployer/Deployer.cs @@ -142,7 +142,7 @@ namespace CodexNetDeployer private RunningContainers? StartMetricsService(CoreInterface ci, List startResults) { - if (!config.MetricsScraper) return null; + if (!config.MetricsScraper || !startResults.Any()) return null; Log("Starting metrics service..."); @@ -176,7 +176,7 @@ namespace CodexNetDeployer private void CheckPeerConnectivity(List codexContainers) { - if (!config.CheckPeerConnection) return; + if (!config.CheckPeerConnection || !codexContainers.Any()) return; Log("Starting peer connectivity check for deployed nodes..."); peerConnectivityChecker.CheckConnectivity(codexContainers); From a84d6a3c22296b80bd155a5b5a88fcbc3c9ef7af Mon Sep 17 00:00:00 2001 From: benbierens Date: Tue, 21 Nov 2023 09:38:58 +0100 Subject: [PATCH 17/25] Replaces debug/info fetching with list of known SPRs, settable via admin commands. --- Tools/BiblioTech/BaseCodexCommand.cs | 27 ------- Tools/BiblioTech/Commands/AdminCommand.cs | 88 ++++++++++++++++++++++- Tools/BiblioTech/Commands/SprCommand.cs | 47 +++++------- Tools/BiblioTech/Program.cs | 5 +- 4 files changed, 106 insertions(+), 61 deletions(-) delete mode 100644 Tools/BiblioTech/BaseCodexCommand.cs diff --git a/Tools/BiblioTech/BaseCodexCommand.cs b/Tools/BiblioTech/BaseCodexCommand.cs deleted file mode 100644 index 0f6780e..0000000 --- a/Tools/BiblioTech/BaseCodexCommand.cs +++ /dev/null @@ -1,27 +0,0 @@ -using BiblioTech.Options; -using CodexPlugin; -using Core; - -namespace BiblioTech -{ - public abstract class BaseCodexCommand : BaseDeploymentCommand - { - private readonly CoreInterface ci; - - public BaseCodexCommand(CoreInterface ci) - { - this.ci = ci; - } - - protected override async Task ExecuteDeploymentCommand(CommandContext context, CodexDeployment codexDeployment) - { - var codexContainers = codexDeployment.CodexInstances.Select(c => c.Containers).ToArray(); - - var group = ci.WrapCodexContainers(codexContainers); - - await Execute(context, group); - } - - protected abstract Task Execute(CommandContext context, ICodexNodeGroup codexGroup); - } -} diff --git a/Tools/BiblioTech/Commands/AdminCommand.cs b/Tools/BiblioTech/Commands/AdminCommand.cs index 9def408..62c3832 100644 --- a/Tools/BiblioTech/Commands/AdminCommand.cs +++ b/Tools/BiblioTech/Commands/AdminCommand.cs @@ -15,11 +15,17 @@ namespace BiblioTech.Commands private readonly WhoIsCommand whoIsCommand = new WhoIsCommand(); private readonly NetInfoCommand netInfoCommand; private readonly DebugPeerCommand debugPeerCommand; + private readonly AddSprCommand addSprCommand; + private readonly ClearSprsCommand clearSprsCommand; + private readonly GetSprCommand getSprCommand; - public AdminCommand(CoreInterface ci) + public AdminCommand(CoreInterface ci, SprCommand sprCommand) { netInfoCommand = new NetInfoCommand(ci); debugPeerCommand = new DebugPeerCommand(ci); + addSprCommand = new AddSprCommand(sprCommand); + clearSprsCommand = new ClearSprsCommand(sprCommand); + getSprCommand = new GetSprCommand(sprCommand); } public override string Name => "admin"; @@ -35,7 +41,10 @@ namespace BiblioTech.Commands deployRemoveCommand, whoIsCommand, netInfoCommand, - debugPeerCommand + debugPeerCommand, + addSprCommand, + clearSprsCommand, + getSprCommand }; protected override async Task Invoke(CommandContext context) @@ -60,6 +69,9 @@ namespace BiblioTech.Commands await whoIsCommand.CommandHandler(context); await netInfoCommand.CommandHandler(context); await debugPeerCommand.CommandHandler(context); + await addSprCommand.CommandHandler(context); + await clearSprsCommand.CommandHandler(context); + await deployUploadCommand.CommandHandler(context); } public class ClearUserAssociationCommand : SubCommandOption @@ -379,5 +391,77 @@ namespace BiblioTech.Commands return content.ToArray(); } } + + public class AddSprCommand : SubCommandOption + { + private readonly SprCommand sprCommand; + private readonly StringOption stringOption = new StringOption("spr", "Codex SPR", true); + + public AddSprCommand(SprCommand sprCommand) + : base(name: "addspr", + description: "Adds a Codex SPR, to be given to users with '/boot'.") + { + this.sprCommand = sprCommand; + } + + public override CommandOption[] Options => new[] { stringOption }; + + protected override async Task onSubCommand(CommandContext context) + { + var spr = await stringOption.Parse(context); + + if (!string.IsNullOrEmpty(spr) ) + { + sprCommand.Add(spr); + await context.Followup("A-OK!"); + } + else + { + await context.Followup("SPR is null or empty."); + } + } + } + + public class ClearSprsCommand : SubCommandOption + { + private readonly SprCommand sprCommand; + private readonly StringOption stringOption = new StringOption("areyousure", "set to 'true' if you are.", true); + + public ClearSprsCommand(SprCommand sprCommand) + : base(name: "clearsprs", + description: "Clears all Codex SPRs in the bot. Users won't be able to use '/boot' till new ones are added.") + { + this.sprCommand = sprCommand; + } + + public override CommandOption[] Options => new[] { stringOption }; + + protected override async Task onSubCommand(CommandContext context) + { + var areyousure = await stringOption.Parse(context); + + if (areyousure != "true") return; + + sprCommand.Clear(); + await context.Followup("Cleared all SPRs."); + } + } + + public class GetSprCommand: SubCommandOption + { + private readonly SprCommand sprCommand; + + public GetSprCommand(SprCommand sprCommand) + : base(name: "getsprs", + description: "Shows all Codex SPRs in the bot.") + { + this.sprCommand = sprCommand; + } + + protected override async Task onSubCommand(CommandContext context) + { + await context.Followup("SPRs: " + string.Join(", ", sprCommand.Get().Select(s => $"'{s}'"))); + } + } } } diff --git a/Tools/BiblioTech/Commands/SprCommand.cs b/Tools/BiblioTech/Commands/SprCommand.cs index ed19c7f..4b3235d 100644 --- a/Tools/BiblioTech/Commands/SprCommand.cs +++ b/Tools/BiblioTech/Commands/SprCommand.cs @@ -1,61 +1,48 @@ using BiblioTech.Options; -using CodexPlugin; -using Core; namespace BiblioTech.Commands { - public class SprCommand : BaseCodexCommand + public class SprCommand : BaseCommand { private readonly Random random = new Random(); - private readonly List sprCache = new List(); - private DateTime lastUpdate = DateTime.MinValue; - - public SprCommand(CoreInterface ci) : base(ci) - { - } + private readonly List knownSprs = new List(); public override string Name => "boot"; public override string StartingMessage => RandomBusyMessage.Get(); public override string Description => "Gets an SPR. (Signed peer record, used for bootstrapping.)"; - protected override async Task OnInvoke(CommandContext context) + protected override async Task Invoke(CommandContext context) { - if (ShouldUpdate()) - { - return true; - } - await ReplyWithRandomSpr(context); - return false; } - protected override async Task Execute(CommandContext context, ICodexNodeGroup codexGroup) + public void Add(string spr) { - lastUpdate = DateTime.UtcNow; - sprCache.Clear(); + if (knownSprs.Contains(spr)) return; + knownSprs.Add(spr); + } - var infos = codexGroup.Select(c => c.GetDebugInfo()).ToArray(); - sprCache.AddRange(infos.Select(i => i.spr)); + public void Clear() + { + knownSprs.Clear(); + } - await ReplyWithRandomSpr(context); + public string[] Get() + { + return knownSprs.ToArray(); } private async Task ReplyWithRandomSpr(CommandContext context) { - if (!sprCache.Any()) + if (!knownSprs.Any()) { await context.Followup("I'm sorry, no SPRs are available... :c"); return; } - var i = random.Next(0, sprCache.Count); - var spr = sprCache[i]; + var i = random.Next(0, knownSprs.Count); + var spr = knownSprs[i]; await context.Followup($"Your SPR: `{spr}`"); } - - private bool ShouldUpdate() - { - return (DateTime.UtcNow - lastUpdate) > TimeSpan.FromMinutes(10); - } } } diff --git a/Tools/BiblioTech/Program.cs b/Tools/BiblioTech/Program.cs index 4bb1175..f027af8 100644 --- a/Tools/BiblioTech/Program.cs +++ b/Tools/BiblioTech/Program.cs @@ -49,12 +49,13 @@ namespace BiblioTech var ci = entryPoint.CreateInterface(); var associateCommand = new UserAssociateCommand(); + var sprCommand = new SprCommand(); var handler = new CommandHandler(client, new GetBalanceCommand(ci, associateCommand), new MintCommand(ci, associateCommand), - new SprCommand(ci), + sprCommand, associateCommand, - new AdminCommand(ci) + new AdminCommand(ci, sprCommand) ); await client.LoginAsync(TokenType.Bot, Config.ApplicationToken); From 46ab3b31ca334c29916aefab13cdcf4d12d92aaf Mon Sep 17 00:00:00 2001 From: benbierens Date: Tue, 21 Nov 2023 10:33:11 +0100 Subject: [PATCH 18/25] Checking that we can bootstrap geth nodes together. --- .../GethPlugin/GethContainerRecipe.cs | 17 ++++++++--------- ProjectPlugins/GethPlugin/GethNode.cs | 12 ++++++++++++ ProjectPlugins/GethPlugin/GethStartupConfig.cs | 6 ++++++ Tests/CodexTests/BasicTests/ExampleTests.cs | 17 +++++++++++++++++ 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/ProjectPlugins/GethPlugin/GethContainerRecipe.cs b/ProjectPlugins/GethPlugin/GethContainerRecipe.cs index beda54c..5af9371 100644 --- a/ProjectPlugins/GethPlugin/GethContainerRecipe.cs +++ b/ProjectPlugins/GethPlugin/GethContainerRecipe.cs @@ -31,8 +31,11 @@ namespace GethPlugin private string CreateArgs(GethStartupConfig config) { - if (config.IsMiner) AddEnvVar("ENABLE_MINER", "1"); - UnlockAccounts(0, 1); + if (config.IsMiner) + { + AddEnvVar("ENABLE_MINER", "1"); + UnlockAccounts(0, 1); + } var httpPort = CreateApiPort(config, tag: HttpPortTag); var discovery = CreateDiscoveryPort(config); @@ -47,16 +50,12 @@ namespace GethPlugin var bootPubKey = config.BootstrapNode.PublicKey; var bootIp = config.BootstrapNode.IpAddress; var bootPort = config.BootstrapNode.Port; - var bootstrapArg = $" --bootnodes enode://{bootPubKey}@{bootIp}:{bootPort} --nat=extip:{bootIp}"; + var bootstrapArg = $" --bootnodes enode://{bootPubKey}@{bootIp}:{bootPort}"; args += bootstrapArg; } if (config.IsPublicTestNet != null) { - AddEnvVar("NAT_PUBLIC_IP_AUTO", "true"); - } - else - { - AddEnvVar("NAT_PUBLIC_IP_AUTO", "false"); + AddEnvVar("NAT_PUBLIC_IP_AUTO", "https://ipinfo.io/ip"); } return args + $" --authrpc.port {authRpc.Number} --ws --ws.addr 0.0.0.0 --ws.port {wsPort.Number}"; @@ -65,7 +64,7 @@ namespace GethPlugin private void UnlockAccounts(int startIndex, int numberOfAccounts) { if (startIndex < 0) throw new ArgumentException(); - if (numberOfAccounts < 1) throw new ArgumentException(); + if (numberOfAccounts < 0) throw new ArgumentException(); if (startIndex + numberOfAccounts > 1000) throw new ArgumentException("Out of accounts!"); AddEnvVar("UNLOCK_START_INDEX", startIndex.ToString()); diff --git a/ProjectPlugins/GethPlugin/GethNode.cs b/ProjectPlugins/GethPlugin/GethNode.cs index 19c3708..050074a 100644 --- a/ProjectPlugins/GethPlugin/GethNode.cs +++ b/ProjectPlugins/GethPlugin/GethNode.cs @@ -19,6 +19,7 @@ namespace GethPlugin void SendTransaction(string contractAddress, TFunction function) where TFunction : FunctionMessage, new(); decimal? GetSyncedBlockNumber(); bool IsContractAvailable(string abi, string contractAddress); + GethBootstrapNode GetBootstrapRecord(); } public class GethNode : IGethNode @@ -69,6 +70,17 @@ namespace GethPlugin StartInteraction().SendTransaction(contractAddress, function); } + public GethBootstrapNode GetBootstrapRecord() + { + var address = StartResult.Container.GetInternalAddress(GethContainerRecipe.ListenPortTag); + + return new GethBootstrapNode( + publicKey: StartResult.PubKey, + ipAddress: address.Host.Replace("http://", ""), + port: address.Port + ); + } + private NethereumInteraction StartInteraction() { var address = StartResult.Container.GetAddress(log, GethContainerRecipe.HttpPortTag); diff --git a/ProjectPlugins/GethPlugin/GethStartupConfig.cs b/ProjectPlugins/GethPlugin/GethStartupConfig.cs index 0d6b86e..51520ca 100644 --- a/ProjectPlugins/GethPlugin/GethStartupConfig.cs +++ b/ProjectPlugins/GethPlugin/GethStartupConfig.cs @@ -3,6 +3,7 @@ public interface IGethSetup { IGethSetup IsMiner(); + IGethSetup WithBootstrapNode(IGethNode node); IGethSetup WithBootstrapNode(GethBootstrapNode node); IGethSetup WithName(string name); IGethSetup AsPublicTestNet(GethTestNetConfig gethTestNetConfig); @@ -15,6 +16,11 @@ public string? NameOverride { get; private set; } public GethTestNetConfig? IsPublicTestNet { get; private set; } + public IGethSetup WithBootstrapNode(IGethNode node) + { + return WithBootstrapNode(node.GetBootstrapRecord()); + } + public IGethSetup WithBootstrapNode(GethBootstrapNode node) { BootstrapNode = node; diff --git a/Tests/CodexTests/BasicTests/ExampleTests.cs b/Tests/CodexTests/BasicTests/ExampleTests.cs index 44c6dc6..d98156c 100644 --- a/Tests/CodexTests/BasicTests/ExampleTests.cs +++ b/Tests/CodexTests/BasicTests/ExampleTests.cs @@ -95,5 +95,22 @@ namespace CodexTests.BasicTests AssertBalance(contracts, seller, Is.GreaterThan(sellerInitialBalance), "Seller was not paid for storage."); AssertBalance(contracts, buyer, Is.LessThan(buyerInitialBalance), "Buyer was not charged for storage."); } + + [Test] + public void GethBootstrapTest() + { + var boot = Ci.StartGethNode(s => s.WithName("boot").IsMiner()); + var disconnected = Ci.StartGethNode(s => s.WithName("disconnected")); + var follow = Ci.StartGethNode(s => s.WithBootstrapNode(boot).WithName("follow")); + + Thread.Sleep(12000); + + var bootN = boot.GetSyncedBlockNumber(); + var discN = disconnected.GetSyncedBlockNumber(); + var followN = follow.GetSyncedBlockNumber(); + + Assert.That(bootN, Is.EqualTo(followN)); + Assert.That(discN, Is.LessThan(bootN)); + } } } From 55811b20da9fcaf35dad132ee8839a783750e945 Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 11 Dec 2023 08:30:05 +0100 Subject: [PATCH 19/25] Fixes validation for not deploying codex nodes --- Tools/CodexNetDeployer/Configuration.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Tools/CodexNetDeployer/Configuration.cs b/Tools/CodexNetDeployer/Configuration.cs index da654fa..1d93402 100644 --- a/Tools/CodexNetDeployer/Configuration.cs +++ b/Tools/CodexNetDeployer/Configuration.cs @@ -144,8 +144,11 @@ namespace CodexNetDeployer if (IsPublicTestNet) { - if (PublicDiscPorts.Split(",").Length != NumberOfCodexNodes) errors.Add("Number of public discovery-ports provided does not match number of codex nodes."); - if (PublicListenPorts.Split(",").Length != NumberOfCodexNodes) errors.Add("Number of public listen-ports provided does not match number of codex nodes."); + if (NumberOfCodexNodes > 0) + { + if (PublicDiscPorts.Split(",").Length != NumberOfCodexNodes) errors.Add("Number of public discovery-ports provided does not match number of codex nodes."); + if (PublicListenPorts.Split(",").Length != NumberOfCodexNodes) errors.Add("Number of public listen-ports provided does not match number of codex nodes."); + } if (PublicGethDiscPort == 0) errors.Add("Geth public discovery port is not set."); if (PublicGethListenPort == 0) errors.Add("Geth public listen port is not set."); } From ec03be69366e58e180c337dbc6191ae96abd8093 Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 11 Dec 2023 08:38:31 +0100 Subject: [PATCH 20/25] Fixes initialization of geth node --- ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs | 2 +- ProjectPlugins/GethPlugin/GethContainerRecipe.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs b/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs index 2e7e84a..87d7f06 100644 --- a/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs +++ b/ProjectPlugins/CodexPlugin/CodexContainerRecipe.cs @@ -50,7 +50,7 @@ namespace CodexPlugin { // This makes the node announce itself to its public IP address. AddEnvVar("NAT_IP_AUTO", "false"); - AddEnvVar("NAT_PUBLIC_IP_AUTO", "https://ipinfo.io/ip"); + AddEnvVar("NAT_PUBLIC_IP_AUTO", PublicIpService.Address); } else { diff --git a/ProjectPlugins/GethPlugin/GethContainerRecipe.cs b/ProjectPlugins/GethPlugin/GethContainerRecipe.cs index 564844f..e4c2fe8 100644 --- a/ProjectPlugins/GethPlugin/GethContainerRecipe.cs +++ b/ProjectPlugins/GethPlugin/GethContainerRecipe.cs @@ -56,7 +56,7 @@ namespace GethPlugin } if (config.IsPublicTestNet != null) { - AddEnvVar("NAT_PUBLIC_IP_AUTO", "https://ipinfo.io/ip"); + AddEnvVar("NAT_PUBLIC_IP_AUTO", PublicIpService.Address); } return args + $" --authrpc.port {authRpc.Number} --ws --ws.addr 0.0.0.0 --ws.port {wsPort.Number}"; From 6d7309cc9c1ed7873a9d20ae3a4da1be04879f48 Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 11 Dec 2023 08:51:46 +0100 Subject: [PATCH 21/25] Update bot for deployment without codex nodes. --- Tools/BiblioTech/Commands/AdminCommand.cs | 8 +++++--- Tools/BiblioTech/DeploymentsFilesMonitor.cs | 3 --- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Tools/BiblioTech/Commands/AdminCommand.cs b/Tools/BiblioTech/Commands/AdminCommand.cs index 62c3832..5e4d478 100644 --- a/Tools/BiblioTech/Commands/AdminCommand.cs +++ b/Tools/BiblioTech/Commands/AdminCommand.cs @@ -136,9 +136,6 @@ namespace BiblioTech.Commands protected override async Task onSubCommand(CommandContext context) { var deployments = Program.DeploymentFilesMonitor.GetDeployments(); - - //todo shows old deployments - if (!deployments.Any()) { await context.Followup("No deployments available."); @@ -269,6 +266,11 @@ namespace BiblioTech.Commands await context.Followup("No deployment found."); return default; } + if (deployment.CodexInstances == null || !deployment.CodexInstances.Any()) + { + await context.Followup("No codex instances were deployed."); + return default; + } try { diff --git a/Tools/BiblioTech/DeploymentsFilesMonitor.cs b/Tools/BiblioTech/DeploymentsFilesMonitor.cs index 0092034..6713d79 100644 --- a/Tools/BiblioTech/DeploymentsFilesMonitor.cs +++ b/Tools/BiblioTech/DeploymentsFilesMonitor.cs @@ -62,9 +62,6 @@ namespace BiblioTech private bool IsDeploymentOk(CodexDeployment? deploy) { if (deploy == null) return false; - if (deploy.CodexInstances == null) return false; - if (!deploy.CodexInstances.Any()) return false; - if (!deploy.CodexInstances.All(i => i.Containers != null && i.Info != null)) return false; if (deploy.GethDeployment == null) return false; if (deploy.GethDeployment.Containers == null) return false; return true; From 777e414f0a5752d254d2db110b68489421558c6d Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 11 Dec 2023 09:59:09 +0100 Subject: [PATCH 22/25] Prevents duplicate deployments. --- Tools/BiblioTech/DeploymentsFilesMonitor.cs | 32 +++++++++++++++++---- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/Tools/BiblioTech/DeploymentsFilesMonitor.cs b/Tools/BiblioTech/DeploymentsFilesMonitor.cs index 6713d79..3b1e52c 100644 --- a/Tools/BiblioTech/DeploymentsFilesMonitor.cs +++ b/Tools/BiblioTech/DeploymentsFilesMonitor.cs @@ -1,6 +1,7 @@ using CodexPlugin; using Discord; using Newtonsoft.Json; +using System.Diagnostics.CodeAnalysis; namespace BiblioTech { @@ -28,11 +29,12 @@ namespace BiblioTech try { var deploy = JsonConvert.DeserializeObject(str); - if (IsDeploymentOk(deploy)) + var names = deployments.Select(d => d.Metadata.Name).ToArray(); + if (deploy != null && IsDeploymentOk(deploy) && !names.Contains(deploy.Metadata.Name)) { var targetFile = Path.Combine(Program.Config.EndpointsPath, Guid.NewGuid().ToString().ToLowerInvariant() + ".json"); File.WriteAllText(targetFile, str); - deployments.Add(deploy); + LoadDeployments(); return true; } } @@ -52,7 +54,7 @@ namespace BiblioTech if (deploy != null && deploy.Metadata.Name == deploymentName) { File.Delete(file); - deployments.Remove(deploy); + LoadDeployments(); return true; } } @@ -69,6 +71,7 @@ namespace BiblioTech private void LoadDeployments() { + deployments.Clear(); var path = Program.Config.EndpointsPath; if (!Directory.Exists(path)) { @@ -78,7 +81,11 @@ namespace BiblioTech } var files = Directory.GetFiles(path); - deployments.AddRange(files.Select(ProcessFile).Where(d => d != null).Cast()); + deployments.AddRange(files + .Select(ProcessFile) + .Where(d => d != null) + .Cast() + .Distinct(new DeploymentNameEqual())); } private CodexDeployment? ProcessFile(string filename) @@ -94,4 +101,19 @@ namespace BiblioTech } } } -} + + internal class DeploymentNameEqual : IEqualityComparer + { + public bool Equals(CodexDeployment? x, CodexDeployment? y) + { + if (x == null && y == null) return true; + if (x == null || y == null) return false; + return x.Metadata.Name == y.Metadata.Name; + } + + public int GetHashCode([DisallowNull] CodexDeployment obj) + { + return obj.Metadata.Name.GetHashCode(); + } + } +} \ No newline at end of file From fde19383df7e47258233e16d62a7bcaad1f777b1 Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 11 Dec 2023 11:01:12 +0100 Subject: [PATCH 23/25] Removes deployment.json from discord bot. --- ProjectPlugins/GethPlugin/GethNode.cs | 79 ++++-- ProjectPlugins/GethPlugin/GethStarter.cs | 2 +- Tools/BiblioTech/BaseDeploymentCommand.cs | 36 --- Tools/BiblioTech/BaseGethCommand.cs | 86 +++++- Tools/BiblioTech/Commands/AdminCommand.cs | 255 +----------------- .../BiblioTech/Commands/GetBalanceCommand.cs | 4 +- Tools/BiblioTech/Commands/MintCommand.cs | 4 +- .../Commands/UserAssociateCommand.cs | 2 - Tools/BiblioTech/DeploymentsFilesMonitor.cs | 119 -------- Tools/BiblioTech/Program.cs | 23 +- Tools/BiblioTech/UserRepo.cs | 1 - 11 files changed, 139 insertions(+), 472 deletions(-) delete mode 100644 Tools/BiblioTech/BaseDeploymentCommand.cs delete mode 100644 Tools/BiblioTech/DeploymentsFilesMonitor.cs diff --git a/ProjectPlugins/GethPlugin/GethNode.cs b/ProjectPlugins/GethPlugin/GethNode.cs index 050074a..36ae392 100644 --- a/ProjectPlugins/GethPlugin/GethNode.cs +++ b/ProjectPlugins/GethPlugin/GethNode.cs @@ -22,11 +22,11 @@ namespace GethPlugin GethBootstrapNode GetBootstrapRecord(); } - public class GethNode : IGethNode + public class DeploymentGethNode : BaseGethNode, IGethNode { private readonly ILog log; - public GethNode(ILog log, GethDeployment startResult) + public DeploymentGethNode(ILog log, GethDeployment startResult) { this.log = log; StartResult = startResult; @@ -35,6 +35,59 @@ namespace GethPlugin public GethDeployment StartResult { get; } public RunningContainer Container => StartResult.Container; + public GethBootstrapNode GetBootstrapRecord() + { + var address = StartResult.Container.GetInternalAddress(GethContainerRecipe.ListenPortTag); + + return new GethBootstrapNode( + publicKey: StartResult.PubKey, + ipAddress: address.Host.Replace("http://", ""), + port: address.Port + ); + } + + protected override NethereumInteraction StartInteraction() + { + var address = StartResult.Container.GetAddress(log, GethContainerRecipe.HttpPortTag); + var account = StartResult.Account; + + var creator = new NethereumInteractionCreator(log, address.Host, address.Port, account.PrivateKey); + return creator.CreateWorkflow(); + } + } + + public class CustomGethNode : BaseGethNode, IGethNode + { + private readonly ILog log; + private readonly string gethHost; + private readonly int gethPort; + private readonly string privateKey; + + public GethDeployment StartResult => throw new NotImplementedException(); + public RunningContainer Container => throw new NotImplementedException(); + + public CustomGethNode(ILog log, string gethHost, int gethPort, string privateKey) + { + this.log = log; + this.gethHost = gethHost; + this.gethPort = gethPort; + this.privateKey = privateKey; + } + + public GethBootstrapNode GetBootstrapRecord() + { + throw new NotImplementedException(); + } + + protected override NethereumInteraction StartInteraction() + { + var creator = new NethereumInteractionCreator(log, gethHost, gethPort, privateKey); + return creator.CreateWorkflow(); + } + } + + public abstract class BaseGethNode + { public Ether GetEthBalance() { return StartInteraction().GetEthBalance().Eth(); @@ -70,26 +123,6 @@ namespace GethPlugin StartInteraction().SendTransaction(contractAddress, function); } - public GethBootstrapNode GetBootstrapRecord() - { - var address = StartResult.Container.GetInternalAddress(GethContainerRecipe.ListenPortTag); - - return new GethBootstrapNode( - publicKey: StartResult.PubKey, - ipAddress: address.Host.Replace("http://", ""), - port: address.Port - ); - } - - private NethereumInteraction StartInteraction() - { - var address = StartResult.Container.GetAddress(log, GethContainerRecipe.HttpPortTag); - var account = StartResult.Account; - - var creator = new NethereumInteractionCreator(log, address.Host, address.Port, account.PrivateKey); - return creator.CreateWorkflow(); - } - public decimal? GetSyncedBlockNumber() { return StartInteraction().GetSyncedBlockNumber(); @@ -99,5 +132,7 @@ namespace GethPlugin { return StartInteraction().IsContractAvailable(abi, contractAddress); } + + protected abstract NethereumInteraction StartInteraction(); } } diff --git a/ProjectPlugins/GethPlugin/GethStarter.cs b/ProjectPlugins/GethPlugin/GethStarter.cs index 5287d9c..c5ad801 100644 --- a/ProjectPlugins/GethPlugin/GethStarter.cs +++ b/ProjectPlugins/GethPlugin/GethStarter.cs @@ -44,7 +44,7 @@ namespace GethPlugin public IGethNode WrapGethContainer(GethDeployment startResult) { startResult = SerializeGate.Gate(startResult); - return new GethNode(tools.GetLog(), startResult); + return new DeploymentGethNode(tools.GetLog(), startResult); } private void Log(string msg) diff --git a/Tools/BiblioTech/BaseDeploymentCommand.cs b/Tools/BiblioTech/BaseDeploymentCommand.cs deleted file mode 100644 index 100c39f..0000000 --- a/Tools/BiblioTech/BaseDeploymentCommand.cs +++ /dev/null @@ -1,36 +0,0 @@ -using BiblioTech.Options; -using CodexPlugin; - -namespace BiblioTech -{ - public abstract class BaseDeploymentCommand : BaseCommand - { - protected override async Task Invoke(CommandContext context) - { - var proceed = await OnInvoke(context); - if (!proceed) return; - - var deployments = Program.DeploymentFilesMonitor.GetDeployments(); - if (deployments.Length == 0) - { - await context.Followup("No deployments are currently available."); - return; - } - if (deployments.Length > 1) - { - await context.Followup("Multiple deployments are online. I don't know which one to pick!"); - return; - } - - var codexDeployment = deployments.Single(); - await ExecuteDeploymentCommand(context, codexDeployment); - } - - protected abstract Task ExecuteDeploymentCommand(CommandContext context, CodexDeployment codexDeployment); - - protected virtual Task OnInvoke(CommandContext context) - { - return Task.FromResult(true); - } - } -} diff --git a/Tools/BiblioTech/BaseGethCommand.cs b/Tools/BiblioTech/BaseGethCommand.cs index 37841b1..52bdb45 100644 --- a/Tools/BiblioTech/BaseGethCommand.cs +++ b/Tools/BiblioTech/BaseGethCommand.cs @@ -1,27 +1,89 @@ using BiblioTech.Options; using CodexContractsPlugin; -using CodexPlugin; -using Core; using GethPlugin; +using Logging; namespace BiblioTech { - public abstract class BaseGethCommand : BaseDeploymentCommand + public static class GethInput { - private readonly CoreInterface ci; + private const string GethHostVar = "GETH_HOST"; + private const string GethPortVar = "GETH_HTTP_PORT"; + private const string GethPrivKeyVar = "GETH_PRIVATE_KEY"; + private const string MarketplaceAddressVar = "CODEXCONTRACTS_MARKETPLACEADDRESS"; + private const string TokenAddressVar = "CODEXCONTRACTS_TOKENADDRESS"; + private const string AbiVar = "CODEXCONTRACTS_ABI"; - public BaseGethCommand(CoreInterface ci) + static GethInput() { - this.ci = ci; + var error = new List(); + var gethHost = GetEnvVar(error, GethHostVar); + var gethPort = Convert.ToInt32(GetEnvVar(error, GethPortVar)); + var privateKey = GetEnvVar(error, GethPrivKeyVar); + var marketplaceAddress = GetEnvVar(error, MarketplaceAddressVar); + var tokenAddress = GetEnvVar(error, TokenAddressVar); + var abi = GetEnvVar(error, AbiVar); + + if (error.Any()) + { + LoadError = string.Join(", ", error); + } + else + { + GethHost = gethHost!; + GethPort = gethPort; + PrivateKey = privateKey!; + MarketplaceAddress = marketplaceAddress!; + TokenAddress = tokenAddress!; + ABI = abi!; + } } - protected override async Task ExecuteDeploymentCommand(CommandContext context, CodexDeployment codexDeployment) - { - var gethDeployment = codexDeployment.GethDeployment; - var contractsDeployment = codexDeployment.CodexContractsDeployment; + public static string GethHost { get; } = string.Empty; + public static int GethPort { get; } + public static string PrivateKey { get; } = string.Empty; + public static string MarketplaceAddress { get; } = string.Empty; + public static string TokenAddress { get; } = string.Empty; + public static string ABI { get; } = string.Empty; + public static string LoadError { get; } = string.Empty; - var gethNode = ci.WrapGethDeployment(gethDeployment); - var contracts = ci.WrapCodexContractsDeployment(gethNode, contractsDeployment); + private static string? GetEnvVar(List error, string name) + { + var result = Environment.GetEnvironmentVariable(name); + if (string.IsNullOrEmpty(result)) error.Add($"'{name}' is not set."); + return result; + } + } + + public abstract class BaseGethCommand : BaseCommand + { + protected override async Task Invoke(CommandContext context) + { + var log = new ConsoleLog(); + + if (!string.IsNullOrEmpty(GethInput.LoadError)) + { + var msg = "Geth input incorrect: " + GethInput.LoadError; + log.Error(msg); + if (IsInAdminChannel(context.Command)) + { + await context.Followup(msg); + } + else + { + await context.Followup("I'm sorry, there seems to be a configuration error."); + } + return; + } + + var contractsDeployment = new CodexContractsDeployment( + marketplaceAddress: GethInput.MarketplaceAddress, + abi: GethInput.ABI, + tokenAddress: GethInput.TokenAddress + ); + + var gethNode = new CustomGethNode(log, GethInput.GethHost, GethInput.GethPort, GethInput.PrivateKey); + var contracts = new CodexContractsAccess(log, gethNode, contractsDeployment); await Execute(context, gethNode, contracts); } diff --git a/Tools/BiblioTech/Commands/AdminCommand.cs b/Tools/BiblioTech/Commands/AdminCommand.cs index 5e4d478..3cb0ae8 100644 --- a/Tools/BiblioTech/Commands/AdminCommand.cs +++ b/Tools/BiblioTech/Commands/AdminCommand.cs @@ -9,20 +9,13 @@ namespace BiblioTech.Commands { private readonly ClearUserAssociationCommand clearCommand = new ClearUserAssociationCommand(); private readonly ReportCommand reportCommand = new ReportCommand(); - private readonly DeployListCommand deployListCommand = new DeployListCommand(); - private readonly DeployUploadCommand deployUploadCommand = new DeployUploadCommand(); - private readonly DeployRemoveCommand deployRemoveCommand = new DeployRemoveCommand(); private readonly WhoIsCommand whoIsCommand = new WhoIsCommand(); - private readonly NetInfoCommand netInfoCommand; - private readonly DebugPeerCommand debugPeerCommand; private readonly AddSprCommand addSprCommand; private readonly ClearSprsCommand clearSprsCommand; private readonly GetSprCommand getSprCommand; - public AdminCommand(CoreInterface ci, SprCommand sprCommand) + public AdminCommand(SprCommand sprCommand) { - netInfoCommand = new NetInfoCommand(ci); - debugPeerCommand = new DebugPeerCommand(ci); addSprCommand = new AddSprCommand(sprCommand); clearSprsCommand = new ClearSprsCommand(sprCommand); getSprCommand = new GetSprCommand(sprCommand); @@ -36,12 +29,7 @@ namespace BiblioTech.Commands { clearCommand, reportCommand, - deployListCommand, - deployUploadCommand, - deployRemoveCommand, whoIsCommand, - netInfoCommand, - debugPeerCommand, addSprCommand, clearSprsCommand, getSprCommand @@ -63,15 +51,9 @@ namespace BiblioTech.Commands await clearCommand.CommandHandler(context); await reportCommand.CommandHandler(context); - await deployListCommand.CommandHandler(context); - await deployUploadCommand.CommandHandler(context); - await deployRemoveCommand.CommandHandler(context); await whoIsCommand.CommandHandler(context); - await netInfoCommand.CommandHandler(context); - await debugPeerCommand.CommandHandler(context); await addSprCommand.CommandHandler(context); await clearSprsCommand.CommandHandler(context); - await deployUploadCommand.CommandHandler(context); } public class ClearUserAssociationCommand : SubCommandOption @@ -126,95 +108,6 @@ namespace BiblioTech.Commands } } - public class DeployListCommand : SubCommandOption - { - public DeployListCommand() - : base("list", "Lists current deployments.") - { - } - - protected override async Task onSubCommand(CommandContext context) - { - var deployments = Program.DeploymentFilesMonitor.GetDeployments(); - if (!deployments.Any()) - { - await context.Followup("No deployments available."); - return; - } - - var nl = Environment.NewLine; - await context.Followup(deployments.Select(FormatDeployment).ToArray()); - } - - private string FormatDeployment(CodexDeployment deployment) - { - var m = deployment.Metadata; - return $"'{m.Name}' ({m.StartUtc.ToString("o")})"; - } - } - - public class DeployUploadCommand : SubCommandOption - { - private readonly FileAttachementOption fileOption = new FileAttachementOption( - name: "json", - description: "Codex-deployment json to add.", - isRequired: true); - - public DeployUploadCommand() - : base("add", "Upload a new deployment JSON file.") - { - } - - public override CommandOption[] Options => new[] { fileOption }; - - protected override async Task onSubCommand(CommandContext context) - { - var file = await fileOption.Parse(context); - if (file == null) return; - - var result = await Program.DeploymentFilesMonitor.DownloadDeployment(file); - if (result) - { - await context.Followup("Success!"); - } - else - { - await context.Followup("That didn't work."); - } - } - } - - public class DeployRemoveCommand : SubCommandOption - { - private readonly StringOption stringOption = new StringOption( - name: "name", - description: "Name of deployment to remove.", - isRequired: true); - - public DeployRemoveCommand() - : base("remove", "Removes a deployment file.") - { - } - - public override CommandOption[] Options => new[] { stringOption }; - - protected override async Task onSubCommand(CommandContext context) - { - var str = await stringOption.Parse(context); - if (string.IsNullOrEmpty(str)) return; - - var result = Program.DeploymentFilesMonitor.DeleteDeployment(str); - if (result) - { - await context.Followup("Success!"); - } - else - { - await context.Followup("That didn't work."); - } - } - } - public class WhoIsCommand : SubCommandOption { private readonly UserOption userOption = new UserOption("User", isRequired: false); @@ -248,152 +141,6 @@ namespace BiblioTech.Commands } } - public abstract class AdminDeploymentCommand : SubCommandOption - { - private readonly CoreInterface ci; - - public AdminDeploymentCommand(CoreInterface ci, string name, string description) - : base(name, description) - { - this.ci = ci; - } - - protected async Task OnDeployment(CommandContext context, Func action) - { - var deployment = Program.DeploymentFilesMonitor.GetDeployments().SingleOrDefault(); - if (deployment == null) - { - await context.Followup("No deployment found."); - return default; - } - if (deployment.CodexInstances == null || !deployment.CodexInstances.Any()) - { - await context.Followup("No codex instances were deployed."); - return default; - } - - try - { - var group = ci.WrapCodexContainers(deployment.CodexInstances.Select(i => i.Containers).ToArray()); - var result = action(group, deployment.Metadata.Name); - return result; - } - catch (Exception ex) - { - var message = new[] - { - "Failed to wrap nodes with exception: " - }; - var exceptionMessage = ex.ToString().Split(Environment.NewLine); - - await context.Followup(message.Concat(exceptionMessage).ToArray()); - return default; - } - } - } - - public class NetInfoCommand : AdminDeploymentCommand - { - public NetInfoCommand(CoreInterface ci) - : base(ci, name: "netinfo", - description: "Fetches info endpoints of codex nodes.") - { - } - - protected override async Task onSubCommand(CommandContext context) - { - var report = await OnDeployment(context, CreateNetInfoReport); - if (report != null && report.Any()) - { - await context.Followup(report); - } - } - - private string[] CreateNetInfoReport(ICodexNodeGroup group, string name) - { - var content = new List - { - $"{DateTime.UtcNow.ToString("o")} - {group.Count()} Codex nodes.", - $"Deployment name: '{name}'." - }; - - foreach (var node in group) - { - try - { - var info = node.GetDebugInfo(); - var json = JsonConvert.SerializeObject(info, Formatting.Indented); - var jsonLines = json.Split(Environment.NewLine); - content.Add($"Node '{node.GetName()}' responded with:"); - content.Add("---"); - content.AddRange(jsonLines); - content.Add("---"); - } - catch (Exception ex) - { - content.Add($"Node '{node.GetName()}' failed to respond with exception: " + ex); - } - } - - return content.ToArray(); - } - } - - public class DebugPeerCommand : AdminDeploymentCommand - { - private readonly StringOption peerIdOption = new StringOption("peerid", "id of peer to try and reach.", true); - - public DebugPeerCommand(CoreInterface ci) - : base(ci, name: "debugpeer", - description: "Calls debug/peer on each codex node.") - { - } - - public override CommandOption[] Options => new[] { peerIdOption }; - - protected override async Task onSubCommand(CommandContext context) - { - var peerId = await peerIdOption.Parse(context); - if (string.IsNullOrEmpty(peerId)) return; - - var report = await OnDeployment(context, (group, name) => CreateDebugPeerReport(group, name, peerId)); - if (report != null && report.Any()) - { - await context.Followup(report); - } - } - - private string[] CreateDebugPeerReport(ICodexNodeGroup group, string name, string peerId) - { - var content = new List - { - $"{DateTime.UtcNow.ToString("o")} - {group.Count()} Codex nodes.", - $"Deployment name: '{name}'.", - $"Calling debug/peer for '{peerId}'." - }; - - foreach (var node in group) - { - try - { - var info = node.GetDebugPeer(peerId); - var json = JsonConvert.SerializeObject(info, Formatting.Indented); - var jsonLines = json.Split(Environment.NewLine); - content.Add($"Node '{node.GetName()}' responded with:"); - content.Add("---"); - content.AddRange(jsonLines); - content.Add("---"); - } - catch (Exception ex) - { - content.Add($"Node '{node.GetName()}' failed to respond with exception: " + ex); - } - } - - return content.ToArray(); - } - } - public class AddSprCommand : SubCommandOption { private readonly SprCommand sprCommand; diff --git a/Tools/BiblioTech/Commands/GetBalanceCommand.cs b/Tools/BiblioTech/Commands/GetBalanceCommand.cs index 05337ab..60118f5 100644 --- a/Tools/BiblioTech/Commands/GetBalanceCommand.cs +++ b/Tools/BiblioTech/Commands/GetBalanceCommand.cs @@ -1,5 +1,6 @@ using BiblioTech.Options; using CodexContractsPlugin; +using CodexPlugin; using Core; using GethPlugin; @@ -12,8 +13,7 @@ namespace BiblioTech.Commands description: "If set, get balance for another user. (Optional, admin-only)", isRequired: false); - public GetBalanceCommand(CoreInterface ci, UserAssociateCommand userAssociateCommand) - : base(ci) + public GetBalanceCommand(UserAssociateCommand userAssociateCommand) { this.userAssociateCommand = userAssociateCommand; } diff --git a/Tools/BiblioTech/Commands/MintCommand.cs b/Tools/BiblioTech/Commands/MintCommand.cs index ed87a1e..cc50a2a 100644 --- a/Tools/BiblioTech/Commands/MintCommand.cs +++ b/Tools/BiblioTech/Commands/MintCommand.cs @@ -1,6 +1,5 @@ using BiblioTech.Options; using CodexContractsPlugin; -using Core; using GethPlugin; namespace BiblioTech.Commands @@ -14,8 +13,7 @@ namespace BiblioTech.Commands isRequired: false); private readonly UserAssociateCommand userAssociateCommand; - public MintCommand(CoreInterface ci, UserAssociateCommand userAssociateCommand) - : base(ci) + public MintCommand(UserAssociateCommand userAssociateCommand) { this.userAssociateCommand = userAssociateCommand; } diff --git a/Tools/BiblioTech/Commands/UserAssociateCommand.cs b/Tools/BiblioTech/Commands/UserAssociateCommand.cs index 2ede721..c23718c 100644 --- a/Tools/BiblioTech/Commands/UserAssociateCommand.cs +++ b/Tools/BiblioTech/Commands/UserAssociateCommand.cs @@ -27,8 +27,6 @@ namespace BiblioTech.Commands return; } - // private commands - var result = Program.UserRepo.AssociateUserWithAddress(user, data); if (result) { diff --git a/Tools/BiblioTech/DeploymentsFilesMonitor.cs b/Tools/BiblioTech/DeploymentsFilesMonitor.cs deleted file mode 100644 index 3b1e52c..0000000 --- a/Tools/BiblioTech/DeploymentsFilesMonitor.cs +++ /dev/null @@ -1,119 +0,0 @@ -using CodexPlugin; -using Discord; -using Newtonsoft.Json; -using System.Diagnostics.CodeAnalysis; - -namespace BiblioTech -{ - public class DeploymentsFilesMonitor - { - private readonly List deployments = new List(); - - public void Initialize() - { - LoadDeployments(); - } - - public CodexDeployment[] GetDeployments() - { - return deployments.ToArray(); - } - - public async Task DownloadDeployment(IAttachment file) - { - using var http = new HttpClient(); - var response = await http.GetAsync(file.Url); - var str = await response.Content.ReadAsStringAsync(); - if (string.IsNullOrEmpty(str)) return false; - - try - { - var deploy = JsonConvert.DeserializeObject(str); - var names = deployments.Select(d => d.Metadata.Name).ToArray(); - if (deploy != null && IsDeploymentOk(deploy) && !names.Contains(deploy.Metadata.Name)) - { - var targetFile = Path.Combine(Program.Config.EndpointsPath, Guid.NewGuid().ToString().ToLowerInvariant() + ".json"); - File.WriteAllText(targetFile, str); - LoadDeployments(); - return true; - } - } - catch { } - return false; - } - - public bool DeleteDeployment(string deploymentName) - { - var path = Program.Config.EndpointsPath; - if (!Directory.Exists(path)) return false; - var files = Directory.GetFiles(path); - - foreach (var file in files) - { - var deploy = ProcessFile(file); - if (deploy != null && deploy.Metadata.Name == deploymentName) - { - File.Delete(file); - LoadDeployments(); - return true; - } - } - return false; - } - - private bool IsDeploymentOk(CodexDeployment? deploy) - { - if (deploy == null) return false; - if (deploy.GethDeployment == null) return false; - if (deploy.GethDeployment.Containers == null) return false; - return true; - } - - private void LoadDeployments() - { - deployments.Clear(); - var path = Program.Config.EndpointsPath; - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path); - File.WriteAllText(Path.Combine(path, "readme.txt"), "Place codex-deployment.json here."); - return; - } - - var files = Directory.GetFiles(path); - deployments.AddRange(files - .Select(ProcessFile) - .Where(d => d != null) - .Cast() - .Distinct(new DeploymentNameEqual())); - } - - private CodexDeployment? ProcessFile(string filename) - { - try - { - var lines = string.Join(" ", File.ReadAllLines(filename)); - return JsonConvert.DeserializeObject(lines); - } - catch - { - return null; - } - } - } - - internal class DeploymentNameEqual : IEqualityComparer - { - public bool Equals(CodexDeployment? x, CodexDeployment? y) - { - if (x == null && y == null) return true; - if (x == null || y == null) return false; - return x.Metadata.Name == y.Metadata.Name; - } - - public int GetHashCode([DisallowNull] CodexDeployment obj) - { - return obj.Metadata.Name.GetHashCode(); - } - } -} \ No newline at end of file diff --git a/Tools/BiblioTech/Program.cs b/Tools/BiblioTech/Program.cs index f027af8..f759d97 100644 --- a/Tools/BiblioTech/Program.cs +++ b/Tools/BiblioTech/Program.cs @@ -1,9 +1,7 @@ using ArgsUniform; using BiblioTech.Commands; -using Core; using Discord; using Discord.WebSocket; -using Logging; namespace BiblioTech { @@ -12,7 +10,6 @@ namespace BiblioTech private DiscordSocketClient client = null!; public static Configuration Config { get; private set; } = null!; - public static DeploymentsFilesMonitor DeploymentFilesMonitor { get; } = new DeploymentsFilesMonitor(); public static UserRepo UserRepo { get; } = new UserRepo(); public static AdminChecker AdminChecker { get; } = new AdminChecker(); @@ -21,8 +18,6 @@ namespace BiblioTech var uniformArgs = new ArgsUniform(PrintHelp, args); Config = uniformArgs.Parse(); - DeploymentFilesMonitor.Initialize(); - EnsurePath(Config.DataPath); EnsurePath(Config.UserDataPath); EnsurePath(Config.EndpointsPath); @@ -36,26 +31,14 @@ namespace BiblioTech client = new DiscordSocketClient(); client.Log += Log; - ProjectPlugin.Load(); - ProjectPlugin.Load(); - ProjectPlugin.Load(); - - var entryPoint = new EntryPoint(new ConsoleLog(), new KubernetesWorkflow.Configuration( - kubeConfigFile: Config.KubeConfigFile, - operationTimeout: TimeSpan.FromMinutes(5), - retryDelay: TimeSpan.FromSeconds(10), - kubernetesNamespace: Config.KubeNamespace), "datafiles"); - - var ci = entryPoint.CreateInterface(); - var associateCommand = new UserAssociateCommand(); var sprCommand = new SprCommand(); var handler = new CommandHandler(client, - new GetBalanceCommand(ci, associateCommand), - new MintCommand(ci, associateCommand), + new GetBalanceCommand(associateCommand), + new MintCommand(associateCommand), sprCommand, associateCommand, - new AdminCommand(ci, sprCommand) + new AdminCommand(sprCommand) ); await client.LoginAsync(TokenType.Bot, Config.ApplicationToken); diff --git a/Tools/BiblioTech/UserRepo.cs b/Tools/BiblioTech/UserRepo.cs index 0db6b6f..237f0a5 100644 --- a/Tools/BiblioTech/UserRepo.cs +++ b/Tools/BiblioTech/UserRepo.cs @@ -2,7 +2,6 @@ using Discord; using GethPlugin; using Newtonsoft.Json; -using Utils; namespace BiblioTech { From c3fb7384e33efb7f664dd86667bba4eef15940c4 Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 11 Dec 2023 11:09:29 +0100 Subject: [PATCH 24/25] Sets up deploying discordbot with geth env vars --- .../DiscordBotContainerRecipe.cs | 8 +++++++ .../DiscordBotStartupConfig.cs | 24 ++++++++++++++++++- Tools/CodexNetDeployer/Deployer.cs | 17 ++++++++++--- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotContainerRecipe.cs b/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotContainerRecipe.cs index 8a5dcd3..ea69f3b 100644 --- a/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotContainerRecipe.cs +++ b/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotContainerRecipe.cs @@ -22,6 +22,14 @@ namespace CodexDiscordBotPlugin AddEnvVar("KUBECONFIG", "/opt/kubeconfig.yaml"); AddEnvVar("KUBENAMESPACE", config.KubeNamespace); + var gethInfo = config.GethInfo; + AddEnvVar("GETH_HOST", gethInfo.Host); + AddEnvVar("GETH_HTTP_PORT", gethInfo.Port.ToString()); + AddEnvVar("GETH_PRIVATE_KEY", gethInfo.PrivKey); + AddEnvVar("CODEXCONTRACTS_MARKETPLACEADDRESS", gethInfo.MarketplaceAddress); + AddEnvVar("CODEXCONTRACTS_TOKENADDRESS", gethInfo.TokenAddress); + AddEnvVar("CODEXCONTRACTS_ABI", gethInfo.Abi); + if (!string.IsNullOrEmpty(config.DataPath)) { AddEnvVar("DATAPATH", config.DataPath); diff --git a/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotStartupConfig.cs b/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotStartupConfig.cs index e590cfa..85c103f 100644 --- a/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotStartupConfig.cs +++ b/ProjectPlugins/CodexDiscordBotPlugin/DiscordBotStartupConfig.cs @@ -2,7 +2,7 @@ { public class DiscordBotStartupConfig { - public DiscordBotStartupConfig(string name, string token, string serverName, string adminRoleName, string adminChannelName, string kubeNamespace) + public DiscordBotStartupConfig(string name, string token, string serverName, string adminRoleName, string adminChannelName, string kubeNamespace, DiscordBotGethInfo gethInfo) { Name = name; Token = token; @@ -10,6 +10,7 @@ AdminRoleName = adminRoleName; AdminChannelName = adminChannelName; KubeNamespace = kubeNamespace; + GethInfo = gethInfo; } public string Name { get; } @@ -18,6 +19,27 @@ public string AdminRoleName { get; } public string AdminChannelName { get; } public string KubeNamespace { get; } + public DiscordBotGethInfo GethInfo { get; } public string? DataPath { get; set; } } + + public class DiscordBotGethInfo + { + public DiscordBotGethInfo(string host, int port, string privKey, string marketplaceAddress, string tokenAddress, string abi) + { + Host = host; + Port = port; + PrivKey = privKey; + MarketplaceAddress = marketplaceAddress; + TokenAddress = tokenAddress; + Abi = abi; + } + + public string Host { get; } + public int Port { get; } + public string PrivKey { get; } + public string MarketplaceAddress { get; } + public string TokenAddress { get; } + public string Abi { get; } + } } diff --git a/Tools/CodexNetDeployer/Deployer.cs b/Tools/CodexNetDeployer/Deployer.cs index bf993ef..1461784 100644 --- a/Tools/CodexNetDeployer/Deployer.cs +++ b/Tools/CodexNetDeployer/Deployer.cs @@ -82,7 +82,7 @@ namespace CodexNetDeployer var codexInstances = CreateCodexInstances(startResults); - var discordBotContainer = DeployDiscordBot(ci); + var discordBotContainer = DeployDiscordBot(ci, gethDeployment, contractsDeployment); return new CodexDeployment(codexInstances, gethDeployment, contractsDeployment, metricsService, discordBotContainer, CreateMetadata(startUtc)); } @@ -120,18 +120,29 @@ namespace CodexNetDeployer }); } - private RunningContainers? DeployDiscordBot(CoreInterface ci) + private RunningContainers? DeployDiscordBot(CoreInterface ci, GethDeployment gethDeployment, CodexContractsDeployment contractsDeployment) { if (!config.DeployDiscordBot) return null; Log("Deploying Discord bot..."); + var addr = gethDeployment.Container.GetInternalAddress(GethContainerRecipe.HttpPortTag); + var info = new DiscordBotGethInfo( + host: addr.Host, + port: addr.Port, + privKey: gethDeployment.Account.PrivateKey, + marketplaceAddress: contractsDeployment.MarketplaceAddress, + tokenAddress: contractsDeployment.TokenAddress, + abi: contractsDeployment.Abi + ); + var rc = ci.DeployCodexDiscordBot(new DiscordBotStartupConfig( name: "discordbot-" + config.DeploymentName, token: config.DiscordBotToken, serverName: config.DiscordBotServerName, adminRoleName: config.DiscordBotAdminRoleName, adminChannelName: config.DiscordBotAdminChannelName, - kubeNamespace: config.KubeNamespace) + kubeNamespace: config.KubeNamespace, + gethInfo: info) { DataPath = config.DiscordBotDataPath }); From 2b21722cf3919eadec8d6b232b1077c7b440bb3e Mon Sep 17 00:00:00 2001 From: benbierens Date: Mon, 11 Dec 2023 11:19:05 +0100 Subject: [PATCH 25/25] forgot to hook up getSPR command --- Tools/BiblioTech/Commands/AdminCommand.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Tools/BiblioTech/Commands/AdminCommand.cs b/Tools/BiblioTech/Commands/AdminCommand.cs index 3cb0ae8..698fb00 100644 --- a/Tools/BiblioTech/Commands/AdminCommand.cs +++ b/Tools/BiblioTech/Commands/AdminCommand.cs @@ -1,7 +1,4 @@ using BiblioTech.Options; -using CodexPlugin; -using Core; -using Newtonsoft.Json; namespace BiblioTech.Commands { @@ -54,6 +51,7 @@ namespace BiblioTech.Commands await whoIsCommand.CommandHandler(context); await addSprCommand.CommandHandler(context); await clearSprsCommand.CommandHandler(context); + await getSprCommand.CommandHandler(context); } public class ClearUserAssociationCommand : SubCommandOption