2
0
mirror of synced 2025-02-10 23:46:53 +00:00

wiring up started

This commit is contained in:
ThatBen 2025-01-15 15:43:50 +01:00
parent e45ed0c21e
commit ee0193c879
No known key found for this signature in database
GPG Key ID: 62C543548433D43E
7 changed files with 183 additions and 105 deletions

View File

@ -1,5 +1,6 @@
using CodexOpenApi;
using Core;
using GethPlugin;
using Logging;
using Newtonsoft.Json;
using Utils;
@ -10,12 +11,14 @@ namespace CodexPlugin
{
private readonly ILog log;
private readonly IPluginTools tools;
private readonly ICodexInstance instance;
private readonly IProcessControl processControl;
private ICodexInstance instance;
private readonly Mapper mapper = new Mapper();
public CodexAccess(IPluginTools tools, ICodexInstance instance, ICrashWatcher crashWatcher)
public CodexAccess(IPluginTools tools, IProcessControl processControl, ICodexInstance instance, ICrashWatcher crashWatcher)
{
this.tools = tools;
this.processControl = processControl;
this.instance = instance;
log = tools.GetLog();
CrashWatcher = crashWatcher;
@ -25,6 +28,14 @@ namespace CodexPlugin
public ICrashWatcher CrashWatcher { get; }
public void Stop(bool waitTillStopped)
{
CrashWatcher.Stop();
processControl.Stop(instance);
// Prevents accidental use after stop:
instance = null!;
}
public string GetImageName()
{
return instance.ImageName;
@ -192,20 +203,18 @@ namespace CodexPlugin
//);
}
public Address? GetMetricsEndpoint()
{
return instance.GetMetricsEndpoint();
}
public EthAccount? GetEthAccount()
{
return instance.GetEthAccount();
}
public void DeleteDataDirFolder()
{
//try
//{
// var containerNumber = Container.Containers.First().Recipe.Number;
// var dataDir = $"datadir{containerNumber}";
// var workflow = tools.CreateWorkflow();
// workflow.ExecuteCommand(Container.Containers.First(), "rm", "-Rfv", $"/codex/{dataDir}/repo");
// Log("Deleted repo folder.");
//}
//catch (Exception e)
//{
// Log("Unable to delete repo folder: " + e);
//}
instance.DeleteDataDirFolder();
}

View File

@ -1,4 +1,8 @@
using Utils;
using Core;
using GethPlugin;
using KubernetesWorkflow.Types;
using Logging;
using Utils;
namespace CodexPlugin
{
@ -10,5 +14,67 @@ namespace CodexPlugin
Address DiscoveryEndpoint { get; }
Address ApiEndpoint { get; }
void DeleteDataDirFolder();
EthAccount? GetEthAccount();
Address? GetMetricsEndpoint();
}
public class CodexContainerInstance : ICodexInstance
{
private readonly RunningContainer container;
private readonly IPluginTools tools;
private readonly ILog log;
private readonly Address? metricsAddress = null;
private readonly EthAccount? ethAccount = null;
public CodexContainerInstance(IPluginTools tools, ILog log, RunningPod pod)
{
container = pod.Containers.Single();
this.tools = tools;
this.log = log;
Name = container.Name;
ImageName = container.Recipe.Image;
StartUtc = container.Recipe.RecipeCreatedUtc;
DiscoveryEndpoint = container.GetAddress(CodexContainerRecipe.DiscoveryPortTag);
ApiEndpoint = container.GetAddress(CodexContainerRecipe.ApiPortTag);
if (pod.StartupConfig.Get<CodexSetup>().MetricsEnabled)
{
metricsAddress = container.GetAddress(CodexContainerRecipe.MetricsPortTag);
}
ethAccount = container.Recipe.Additionals.Get<EthAccount>();
}
public string Name { get; }
public string ImageName { get; }
public DateTime StartUtc { get; }
public Address DiscoveryEndpoint { get; }
public Address ApiEndpoint { get; }
public void DeleteDataDirFolder()
{
try
{
var dataDirVar = container.Recipe.EnvVars.Single(e => e.Name == "CODEX_DATA_DIR");
var dataDir = dataDirVar.Value;
var workflow = tools.CreateWorkflow();
workflow.ExecuteCommand(container, "rm", "-Rfv", $"/codex/{dataDir}/repo");
log.Log("Deleted repo folder.");
}
catch (Exception e)
{
log.Log("Unable to delete repo folder: " + e);
}
}
public EthAccount? GetEthAccount()
{
return ethAccount;
}
public Address? GetMetricsEndpoint()
{
return metricsAddress;
}
}
}

View File

@ -52,18 +52,15 @@ namespace CodexPlugin
private readonly ILog log;
private readonly IPluginTools tools;
private readonly ICodexNodeHooks hooks;
private readonly EthAccount? ethAccount;
private readonly TransferSpeeds transferSpeeds;
private string peerId = string.Empty;
private string nodeId = string.Empty;
private readonly CodexAccess codexAccess;
public CodexNode(IPluginTools tools, CodexAccess codexAccess, CodexNodeGroup group, IMarketplaceAccess marketplaceAccess, ICodexNodeHooks hooks, EthAccount? ethAccount)
public CodexNode(IPluginTools tools, CodexAccess codexAccess, IMarketplaceAccess marketplaceAccess, ICodexNodeHooks hooks)
{
this.tools = tools;
this.ethAccount = ethAccount;
this.codexAccess = codexAccess;
Group = group;
Marketplace = marketplaceAccess;
this.hooks = hooks;
Version = new DebugInfoVersion();
@ -74,15 +71,17 @@ namespace CodexPlugin
public void Awake()
{
hooks.OnNodeStarting(codexAccess.GetStartUtc(), codexAccess.GetImageName(), ethAccount);
hooks.OnNodeStarting(codexAccess.GetStartUtc(), codexAccess.GetImageName(), codexAccess.GetEthAccount());
}
public void Initialize()
{
InitializePeerNodeId();
InitializeLogReplacements();
hooks.OnNodeStarted(peerId, nodeId);
}
public CodexNodeGroup Group { get; }
public IMarketplaceAccess Marketplace { get; }
public DebugInfoVersion Version { get; private set; }
public ITransferSpeeds TransferSpeeds { get => transferSpeeds; }
@ -91,8 +90,9 @@ namespace CodexPlugin
{
get
{
throw new Exception("todo");
//return new MetricsScrapeTarget(CodexAccess.Container.Containers.First(), CodexContainerRecipe.MetricsPortTag);
var address = codexAccess.GetMetricsEndpoint();
if (address == null) throw new Exception("Metrics ScrapeTarget accessed, but node was not started with EnableMetrics()");
return address;
}
}
@ -101,7 +101,7 @@ namespace CodexPlugin
get
{
EnsureMarketplace();
return ethAccount!.EthAddress;
return codexAccess.GetEthAccount()!.EthAddress;
}
}
@ -110,7 +110,7 @@ namespace CodexPlugin
get
{
EnsureMarketplace();
return ethAccount!;
return codexAccess.GetEthAccount()!;
}
}
@ -261,27 +261,7 @@ namespace CodexPlugin
{
Log("Stopping...");
hooks.OnNodeStopping();
codexAccess.CrashWatcher.Stop();
Group.Stop(this, waitTillStopped);
}
public void EnsureOnlineGetVersionResponse()
{
var debugInfo = Time.Retry(codexAccess.GetDebugInfo, "ensure online");
peerId = debugInfo.Id;
nodeId = debugInfo.Table.LocalNode.NodeId;
var nodeName = codexAccess.Container.Name;
if (!debugInfo.Version.IsValid())
{
throw new Exception($"Invalid version information received from Codex node {GetName()}: {debugInfo.Version}");
}
log.AddStringReplace(peerId, nodeName);
log.AddStringReplace(CodexUtils.ToShortId(peerId), nodeName);
log.AddStringReplace(debugInfo.Table.LocalNode.NodeId, nodeName);
log.AddStringReplace(CodexUtils.ToShortId(debugInfo.Table.LocalNode.NodeId), nodeName);
Version = debugInfo.Version;
codexAccess.Stop(waitTillStopped);
}
public Address GetDiscoveryEndpoint()
@ -299,6 +279,29 @@ namespace CodexPlugin
return $"CodexNode:{GetName()}";
}
private void InitializePeerNodeId()
{
var debugInfo = Time.Retry(codexAccess.GetDebugInfo, "ensure online");
if (!debugInfo.Version.IsValid())
{
throw new Exception($"Invalid version information received from Codex node {GetName()}: {debugInfo.Version}");
}
peerId = debugInfo.Id;
nodeId = debugInfo.Table.LocalNode.NodeId;
Version = debugInfo.Version;
}
private void InitializeLogReplacements()
{
var nodeName = GetName();
log.AddStringReplace(peerId, nodeName);
log.AddStringReplace(CodexUtils.ToShortId(peerId), nodeName);
log.AddStringReplace(nodeId, nodeName);
log.AddStringReplace(CodexUtils.ToShortId(nodeId), nodeName);
}
private string[] GetPeerMultiAddresses(CodexNode peer, DebugInfo peerInfo)
{
var peerId = peer.GetDiscoveryEndpoint().Host
@ -377,7 +380,7 @@ namespace CodexPlugin
private void EnsureMarketplace()
{
if (ethAccount == null) throw new Exception("Marketplace is not enabled for this Codex node. Please start it with the option '.EnableMarketplace(...)' to enable it.");
if (codexAccess.GetEthAccount() == null) throw new Exception("Marketplace is not enabled for this Codex node. Please start it with the option '.EnableMarketplace(...)' to enable it.");
}
private void Log(string msg)

View File

@ -1,15 +1,11 @@
using CodexPlugin.Hooks;
using Core;
using GethPlugin;
using KubernetesWorkflow;
using KubernetesWorkflow.Types;
namespace CodexPlugin
{
public interface ICodexNodeFactory
{
CodexNode CreateOnlineCodexNode(CodexAccess access, CodexNodeGroup group);
ContainerCrashWatcher CreateCrashWatcher(RunningContainer c);
CodexNode CreateOnlineCodexNode(CodexAccess access);
}
public class CodexNodeFactory : ICodexNodeFactory
@ -23,31 +19,17 @@ namespace CodexPlugin
this.codexHooksFactory = codexHooksFactory;
}
public CodexNode CreateOnlineCodexNode(CodexAccess access, CodexNodeGroup group)
public CodexNode CreateOnlineCodexNode(CodexAccess access)
{
var ethAccount = GetEthAccount(access);
var hooks = codexHooksFactory.CreateHooks(access.Container.Name);
var marketplaceAccess = GetMarketplaceAccess(access, ethAccount, hooks);
return new CodexNode(tools, access, group, marketplaceAccess, hooks, ethAccount);
var hooks = codexHooksFactory.CreateHooks(access.GetName());
var marketplaceAccess = GetMarketplaceAccess(access, hooks);
return new CodexNode(tools, access, marketplaceAccess, hooks);
}
private IMarketplaceAccess GetMarketplaceAccess(CodexAccess codexAccess, EthAccount? ethAccount, ICodexNodeHooks hooks)
private IMarketplaceAccess GetMarketplaceAccess(CodexAccess codexAccess, ICodexNodeHooks hooks)
{
if (ethAccount == null) return new MarketplaceUnavailable();
if (codexAccess.GetEthAccount() == null) return new MarketplaceUnavailable();
return new MarketplaceAccess(tools.GetLog(), codexAccess, hooks);
}
private EthAccount? GetEthAccount(CodexAccess access)
{
var ethAccount = access.Container.Containers.Single().Recipe.Additionals.Get<EthAccount>();
if (ethAccount == null) return null;
return ethAccount;
}
public ContainerCrashWatcher CreateCrashWatcher(RunningContainer c)
{
return tools.CreateWorkflow().CreateCrashWatcher(c);
}
}
}

View File

@ -7,18 +7,16 @@ namespace CodexPlugin
{
public interface ICodexNodeGroup : IEnumerable<ICodexNode>, IHasManyMetricScrapeTargets
{
void BringOffline(bool waitTillStopped);
void Stop(bool waitTillStopped);
ICodexNode this[int index] { get; }
}
public class CodexNodeGroup : ICodexNodeGroup
{
private readonly CodexStarter starter;
private CodexNode[] nodes;
private readonly CodexNode[] nodes;
public CodexNodeGroup(CodexStarter starter, IPluginTools tools, CodexNode[] nodes)
public CodexNodeGroup(IPluginTools tools, CodexNode[] nodes)
{
this.starter = starter;
this.nodes = nodes;
Version = new DebugInfoVersion();
}
@ -31,17 +29,14 @@ namespace CodexPlugin
}
}
public void BringOffline(bool waitTillStopped)
public void Stop(bool waitTillStopped)
{
starter.BringOffline(this, waitTillStopped);
// Clear everything. Prevent accidental use.
nodes = Array.Empty<CodexNode>();
foreach (var node in Nodes) node.Stop(waitTillStopped);
}
public void Stop(CodexNode node, bool waitTillStopped)
{
starter.Stop(node, waitTillStopped);
nodes = nodes.Where(n => n != node).ToArray();
node.Stop(waitTillStopped);
}
public ICodexNode[] Nodes => nodes;
@ -65,7 +60,7 @@ namespace CodexPlugin
public void EnsureOnline()
{
foreach (var node in nodes) node.EnsureOnlineGetVersionResponse();
foreach (var node in nodes) node.Initialize();
var versionResponses = Nodes.Select(n => n.Version);
var first = versionResponses.First();
@ -76,7 +71,6 @@ namespace CodexPlugin
}
Version = first;
foreach (var node in nodes) node.Initialize();
}
}
}

View File

@ -4,14 +4,16 @@ using GethPlugin;
using KubernetesWorkflow;
using KubernetesWorkflow.Types;
using Logging;
using Utils;
namespace CodexPlugin
{
public class CodexStarter
public class CodexStarter : IProcessControl
{
private readonly IPluginTools pluginTools;
private readonly CodexContainerRecipe recipe = new CodexContainerRecipe();
private readonly ApiChecker apiChecker;
private readonly Dictionary<ICodexInstance, RunningPod> podMap = new Dictionary<ICodexInstance, RunningPod>();
private DebugInfoVersion? versionResponse;
public CodexStarter(IPluginTools pluginTools)
@ -46,6 +48,17 @@ namespace CodexPlugin
return containers;
}
public void Stop(ICodexInstance instance, bool waitTillStopped)
{
Log($"Stopping node...");
var pod = podMap[instance];
podMap.Remove(instance);
var workflow = pluginTools.CreateWorkflow();
workflow.Stop(pod, waitTillStopped);
Log("Stopped.");
}
public ICodexNodeGroup WrapCodexContainers(CoreInterface coreInterface, RunningPod[] containers)
{
var codexNodeFactory = new CodexNodeFactory(pluginTools, HooksFactory);
@ -58,24 +71,6 @@ namespace CodexPlugin
return group;
}
public void BringOffline(CodexNodeGroup group, bool waitTillStopped)
{
Log($"Stopping {group.Describe()}...");
foreach (var node in group)
{
node.Stop(waitTillStopped);
}
Log("Stopped.");
}
public void Stop(CodexNode pod, bool waitTillStopped)
{
Log($"Stopping node...");
var workflow = pluginTools.CreateWorkflow();
workflow.Stop(pod, waitTillStopped);
Log("Stopped.");
}
public string GetCodexId()
{
if (versionResponse != null) return versionResponse.Version;
@ -118,7 +113,10 @@ namespace CodexPlugin
private CodexNodeGroup CreateCodexGroup(CoreInterface coreInterface, RunningPod[] runningContainers, CodexNodeFactory codexNodeFactory)
{
var group = new CodexNodeGroup(this, pluginTools, runningContainers, codexNodeFactory);
var instances = runningContainers.Select(CreateInstance).ToArray();
var accesses = instances.Select(CreateAccess).ToArray();
var nodes = accesses.Select(codexNodeFactory.CreateOnlineCodexNode).ToArray();
var group = new CodexNodeGroup(pluginTools, nodes);
try
{
@ -133,6 +131,25 @@ namespace CodexPlugin
return group;
}
private CodexAccess CreateAccess(ICodexInstance instance)
{
var crashWatcher = CreateCrashWatcher(instance);
return new CodexAccess(pluginTools, this, instance, crashWatcher);
}
private ICrashWatcher CreateCrashWatcher(ICodexInstance instance)
{
var pod = podMap[instance];
return pluginTools.CreateWorkflow().CreateCrashWatcher(pod.Containers.Single());
}
private ICodexInstance CreateInstance(RunningPod pod)
{
var instance = new CodexContainerInstance(pluginTools, pluginTools.GetLog(), pod);
podMap.Add(instance, pod);
return instance;
}
private void CodexNodesNotOnline(CoreInterface coreInterface, RunningPod[] runningContainers)
{
Log("Codex nodes failed to start");

View File

@ -0,0 +1,7 @@
namespace CodexPlugin
{
public interface IProcessControl
{
void Stop(ICodexInstance instance, bool waitTillStopped);
}
}