Very basic endpoint pinging that might not even work.

This commit is contained in:
benbierens 2023-10-18 14:59:39 +02:00
parent 888b19d8e5
commit 766e2f5c20
No known key found for this signature in database
GPG Key ID: FE44815D96D0A1AA
11 changed files with 239 additions and 137 deletions

View File

@ -0,0 +1,10 @@
using Utils;
namespace BiblioTech
{
public class CodexEndpoints
{
public string Name { get; set; } = string.Empty;
public Address[] Addresses { get; set; } = Array.Empty<Address>();
}
}

View File

@ -1,61 +0,0 @@
using Discord.Commands;
using Discord.WebSocket;
using System.Reflection;
namespace BiblioTech
{
public class CommandHandler
{
private readonly DiscordSocketClient client;
private readonly CommandService commands;
// Retrieve client and CommandService instance via ctor
public CommandHandler(DiscordSocketClient client, CommandService commands)
{
this.commands = commands;
this.client = client;
}
public async Task InstallCommandsAsync()
{
// Hook the MessageReceived event into our command handler
client.MessageReceived += HandleCommandAsync;
// Here we discover all of the command modules in the entry
// assembly and load them. Starting from Discord.NET 2.0, a
// service provider is required to be passed into the
// module registration method to inject the
// required dependencies.
//
// If you do not use Dependency Injection, pass null.
// See Dependency Injection guide for more information.
await commands.AddModulesAsync(assembly: Assembly.GetEntryAssembly(),
services: null);
}
private async Task HandleCommandAsync(SocketMessage messageParam)
{
// Don't process the command if it was a system message
var message = messageParam as SocketUserMessage;
if (message == null) return;
// Create a number to track where the prefix ends and the command begins
int argPos = 0;
// Determine if the message is a command based on the prefix and make sure no bots trigger commands
if (message.Author.IsBot) return;
if (!message.HasMentionPrefix(client.CurrentUser, ref argPos) &&
!message.Content.StartsWith("!")) return;
// Create a WebSocket-based command context based on the message
var context = new SocketCommandContext(client, message);
// Execute the command with the command context we just
// created, along with the service provider for precondition checks.
await commands.ExecuteAsync(
context: context,
argPos: argPos,
services: null);
}
}
}

View File

@ -7,7 +7,10 @@ namespace BiblioTech
[Uniform("token", "t", "TOKEN", true, "Discord Application Token")]
public string ApplicationToken { get; set; } = string.Empty;
[Uniform("deploys", "d", "DEPLOYS", false, "Path where deployment JSONs are located.")]
public string DeploymentsPath { get; set; } = "deploys";
[Uniform("server-name", "sn", "SERVERNAME", true, "Name of the Discord server")]
public string ServerName { get; set; } = string.Empty;
[Uniform("endpoints", "e", "ENDPOINTS", false, "Path where endpoint JSONs are located. Also accepts codex-deployment JSONs.")]
public string EndpointsPath { get; set; } = "endpoints";
}
}

View File

@ -1,47 +0,0 @@
using CodexPlugin;
using Newtonsoft.Json;
namespace BiblioTech
{
public class DeploymentFilesMonitor
{
private DateTime lastUpdate = DateTime.MinValue;
private CodexDeployment[] deployments = Array.Empty<CodexDeployment>();
public CodexDeployment[] GetDeployments()
{
if (ShouldUpdate())
{
UpdateDeployments();
}
return deployments;
}
private void UpdateDeployments()
{
lastUpdate = DateTime.UtcNow;
var path = Program.Config.DeploymentsPath;
var files = Directory.GetFiles(path);
deployments = files.Select(ProcessFile).Where(d => d != null).Cast<CodexDeployment>().ToArray();
}
private CodexDeployment? ProcessFile(string filename)
{
try
{
var lines = File.ReadAllLines(filename);
return JsonConvert.DeserializeObject<CodexDeployment>(string.Join(" ", lines));
}
catch
{
return null;
}
}
private bool ShouldUpdate()
{
return !deployments.Any() || (DateTime.UtcNow - lastUpdate) > TimeSpan.FromMinutes(10);
}
}
}

View File

@ -0,0 +1,78 @@
using CodexPlugin;
using KubernetesWorkflow;
using Newtonsoft.Json;
using Utils;
namespace BiblioTech
{
public class EndpointsFilesMonitor
{
private DateTime lastUpdate = DateTime.MinValue;
private CodexEndpoints[] endpoints = Array.Empty<CodexEndpoints>();
public CodexEndpoints[] GetEndpoints()
{
if (ShouldUpdate())
{
UpdateEndpoints();
}
return endpoints;
}
private void UpdateEndpoints()
{
lastUpdate = DateTime.UtcNow;
var path = Program.Config.EndpointsPath;
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
File.WriteAllText(Path.Combine(path, "readme.txt"), "Place codex-deployment.json or codex-endpoints.json here.");
return;
}
var files = Directory.GetFiles(path);
endpoints = files.Select(ProcessFile).Where(d => d != null).Cast<CodexEndpoints>().ToArray();
}
private CodexEndpoints? ProcessFile(string filename)
{
try
{
var lines = string.Join(" ", File.ReadAllLines(filename));
try
{
return JsonConvert.DeserializeObject<CodexEndpoints>(lines);
}
catch { }
return ConvertToEndpoints(JsonConvert.DeserializeObject<CodexDeployment>(lines));
}
catch
{
return null;
}
}
private CodexEndpoints? ConvertToEndpoints(CodexDeployment? codexDeployment)
{
if (codexDeployment == null) return null;
return new CodexEndpoints
{
Name = "codex-deployment-" + codexDeployment.Metadata.StartUtc.ToString("o"),
Addresses = codexDeployment.CodexContainers.Select(ConvertToAddress).ToArray()
};
}
private Address ConvertToAddress(RunningContainer rc)
{
return rc.ClusterExternalAddress;
}
private bool ShouldUpdate()
{
return !endpoints.Any() || (DateTime.UtcNow - lastUpdate) > TimeSpan.FromMinutes(10);
}
}
}

View File

@ -0,0 +1,81 @@
using CodexPlugin;
using Core;
using KubernetesWorkflow;
namespace BiblioTech
{
public class EndpointsMonitor
{
private readonly EndpointsFilesMonitor fileMonitor;
private readonly EntryPoint entryPoint;
private DateTime lastUpdate = DateTime.MinValue;
private string report = string.Empty;
public EndpointsMonitor(EndpointsFilesMonitor fileMonitor, EntryPoint entryPoint)
{
this.fileMonitor = fileMonitor;
this.entryPoint = entryPoint;
}
public async Task<string> GetReport()
{
if (ShouldUpdate())
{
await UpdateReport();
}
return report;
}
private async Task UpdateReport()
{
lastUpdate = DateTime.UtcNow;
var endpoints = fileMonitor.GetEndpoints();
if (!endpoints.Any())
{
report = "There are no networks currently online.";
}
else
{
var nl = Environment.NewLine;
report = $"There are {endpoints.Length} networks online." + nl;
for (var i = 0; i < endpoints.Length; i++)
{
var e = endpoints[i];
report += $" [{i} - {e.Name}] = {await GetStatusMessage(e)}";
}
}
}
private async Task<string> GetStatusMessage(CodexEndpoints e)
{
var success = 0;
foreach (var addr in e.Addresses)
{
await Task.Run(() =>
{
// this is definitely not going to work:
var rc = new RunningContainer(null!, null!, null!, "", addr, addr);
var access = new CodexAccess(entryPoint.Tools, rc, null!);
var debugInfo = access.GetDebugInfo();
if (!string.IsNullOrEmpty(debugInfo.id))
{
success++;
}
});
}
return $"{success} / {e.Addresses.Length} online.";
// todo: analyze returned peerIDs to say something about
// the number of none-test-net managed nodes seen on the network.
}
private bool ShouldUpdate()
{
return string.IsNullOrEmpty(report) || (DateTime.UtcNow - lastUpdate) > TimeSpan.FromMinutes(10);
}
}
}

View File

@ -61,8 +61,5 @@ namespace BiblioTech
Console.WriteLine(json);
}
}
}
}

View File

@ -1,14 +0,0 @@
using Discord.Commands;
namespace BiblioTech
{
public class HelloWorldModule : ModuleBase<SocketCommandContext>
{
[Command("say")]
[Summary("Echoes a message.")]
public Task SayAsync([Remainder][Summary("The text to echo")] string echo)
{
return ReplyAsync("I say: " + echo);
}
}
}

View File

@ -1,9 +1,8 @@
using ArgsUniform;
using Core;
using Discord;
using Discord.Commands;
using Discord.Net;
using Discord.WebSocket;
using Newtonsoft.Json;
using Logging;
namespace BiblioTech
{
@ -12,7 +11,7 @@ namespace BiblioTech
private DiscordSocketClient client = null!;
public static Configuration Config { get; private set; } = null!;
public static DeploymentFilesMonitor DeploymentFilesMonitor { get; } = new DeploymentFilesMonitor();
public static EndpointsFilesMonitor DeploymentFilesMonitor { get; } = new EndpointsFilesMonitor();
public static Task Main(string[] args)
{
@ -28,12 +27,18 @@ namespace BiblioTech
client = new DiscordSocketClient();
client.Log += Log;
var helloWorld = new HelloWorldCommand(client);
ProjectPlugin.Load<CodexPlugin.CodexPlugin>();
var entryPoint = new EntryPoint(new ConsoleLog(), new KubernetesWorkflow.Configuration(
kubeConfigFile: null, // todo: readonly file
operationTimeout: TimeSpan.FromMinutes(5),
retryDelay: TimeSpan.FromSeconds(10),
kubernetesNamespace: "not-applicable"), "datafiles");
//var cmdService = new CommandService();
//var handler = new CommandHandler(client, cmdService);
//await handler.InstallCommandsAsync();
//Console.WriteLine("Command handler installed...");
var fileMonitor = new EndpointsFilesMonitor();
var monitor = new EndpointsMonitor(fileMonitor, entryPoint);
var statusCommand = new StatusCommand(client, monitor);
//var helloWorld = new HelloWorldCommand(client); Example for how to do arguments.
await client.LoginAsync(TokenType.Bot, Config.ApplicationToken);
await client.StartAsync();

View File

@ -0,0 +1,50 @@
using Discord.Net;
using Discord.WebSocket;
using Discord;
using Newtonsoft.Json;
using Core;
namespace BiblioTech
{
public class StatusCommand
{
private const string CommandName = "status";
private readonly DiscordSocketClient client;
private readonly EndpointsMonitor monitor;
public StatusCommand(DiscordSocketClient client, EndpointsMonitor monitor)
{
this.client = client;
this.monitor = monitor;
client.Ready += Client_Ready;
client.SlashCommandExecuted += SlashCommandHandler;
}
private async Task SlashCommandHandler(SocketSlashCommand command)
{
if (command.CommandName != CommandName) return;
await command.RespondAsync(await monitor.GetReport());
}
private async Task Client_Ready()
{
var guild = client.Guilds.Single(g => g.Name == Program.Config.ServerName);
var guildCommand = new SlashCommandBuilder()
.WithName(CommandName)
.WithDescription("Display status of test net.");
try
{
await guild.CreateApplicationCommandAsync(guildCommand.Build());
}
catch (HttpException exception)
{
var json = JsonConvert.SerializeObject(exception.Errors, Formatting.Indented);
Console.WriteLine(json);
}
}
}
}

View File

@ -3,4 +3,4 @@ services:
image: thatbenbierens/codex-discordbot:initial
environment:
- TOKEN=tokenplz
- DEPLOYS=deploypath
- SERVERNAME=ThatBen's server