Removes deployment.json from discord bot.

This commit is contained in:
benbierens 2023-12-11 11:01:12 +01:00
parent 777e414f0a
commit fde19383df
No known key found for this signature in database
GPG Key ID: FE44815D96D0A1AA
11 changed files with 139 additions and 472 deletions

View File

@ -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();
}
}

View File

@ -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)

View File

@ -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<bool> OnInvoke(CommandContext context)
{
return Task.FromResult(true);
}
}
}

View File

@ -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<string>();
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<string> 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);
}

View File

@ -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<T?> OnDeployment<T>(CommandContext context, Func<ICodexNodeGroup, string, T> 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<string>
{
$"{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<string>
{
$"{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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -27,8 +27,6 @@ namespace BiblioTech.Commands
return;
}
// private commands
var result = Program.UserRepo.AssociateUserWithAddress(user, data);
if (result)
{

View File

@ -1,119 +0,0 @@
using CodexPlugin;
using Discord;
using Newtonsoft.Json;
using System.Diagnostics.CodeAnalysis;
namespace BiblioTech
{
public class DeploymentsFilesMonitor
{
private readonly List<CodexDeployment> deployments = new List<CodexDeployment>();
public void Initialize()
{
LoadDeployments();
}
public CodexDeployment[] GetDeployments()
{
return deployments.ToArray();
}
public async Task<bool> 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<CodexDeployment>(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<CodexDeployment>()
.Distinct(new DeploymentNameEqual()));
}
private CodexDeployment? ProcessFile(string filename)
{
try
{
var lines = string.Join(" ", File.ReadAllLines(filename));
return JsonConvert.DeserializeObject<CodexDeployment>(lines);
}
catch
{
return null;
}
}
}
internal class DeploymentNameEqual : IEqualityComparer<CodexDeployment>
{
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();
}
}
}

View File

@ -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<Configuration>(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<CodexPlugin.CodexPlugin>();
ProjectPlugin.Load<GethPlugin.GethPlugin>();
ProjectPlugin.Load<CodexContractsPlugin.CodexContractsPlugin>();
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);

View File

@ -2,7 +2,6 @@
using Discord;
using GethPlugin;
using Newtonsoft.Json;
using Utils;
namespace BiblioTech
{