From 2292d2e672c4ce82b23bf31f5faf1b1a32615604 Mon Sep 17 00:00:00 2001 From: ThatBen Date: Wed, 15 Jan 2025 14:15:40 +0100 Subject: [PATCH 01/25] Removes container-related concepts from CodexNode type --- ProjectPlugins/CodexPlugin/CodexNode.cs | 24 +++++++++++++------ ProjectPlugins/CodexPlugin/CodexStarter.cs | 14 ++--------- Tests/CodexContinuousTests/SingleTestRun.cs | 1 - .../Helpers/PeerConnectionTestHelpers.cs | 7 +----- Tools/CodexNetDeployer/Deployer.cs | 6 ++--- 5 files changed, 22 insertions(+), 30 deletions(-) diff --git a/ProjectPlugins/CodexPlugin/CodexNode.cs b/ProjectPlugins/CodexPlugin/CodexNode.cs index afb78f34..94404404 100644 --- a/ProjectPlugins/CodexPlugin/CodexNode.cs +++ b/ProjectPlugins/CodexPlugin/CodexNode.cs @@ -34,17 +34,18 @@ namespace CodexPlugin void ConnectToPeer(ICodexNode node); DebugInfoVersion Version { get; } IMarketplaceAccess Marketplace { get; } - CrashWatcher CrashWatcher { get; } - PodInfo GetPodInfo(); ITransferSpeeds TransferSpeeds { get; } EthAccount EthAccount { get; } + Address GetDiscoveryEndpoint(); + /// /// Warning! The node is not usable after this. /// TODO: Replace with delete-blocks debug call once available in Codex. /// void DeleteRepoFolder(); void Stop(bool waitTillStopped); + bool HasCrashed(); } public class CodexNode : ICodexNode @@ -256,11 +257,6 @@ namespace CodexPlugin Log($"Successfully connected to peer {peer.GetName()}."); } - public PodInfo GetPodInfo() - { - return CodexAccess.GetPodInfo(); - } - public void DeleteRepoFolder() { CodexAccess.DeleteRepoFolder(); @@ -294,6 +290,20 @@ namespace CodexPlugin Version = debugInfo.Version; } + public Address GetDiscoveryEndpoint() + { + var info = CodexAccess.GetPodInfo(); + return new Address( + host: info.Ip, + port: Container.Recipe.GetPortByTag(CodexContainerRecipe.DiscoveryPortTag)!.Number + ); + } + + public bool HasCrashed() + { + return CrashWatcher.HasContainerCrashed(); + } + public override string ToString() { return $"CodexNode:{GetName()}"; diff --git a/ProjectPlugins/CodexPlugin/CodexStarter.cs b/ProjectPlugins/CodexPlugin/CodexStarter.cs index 4c433725..1aebc72e 100644 --- a/ProjectPlugins/CodexPlugin/CodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/CodexStarter.cs @@ -61,11 +61,9 @@ namespace CodexPlugin public void BringOffline(CodexNodeGroup group, bool waitTillStopped) { Log($"Stopping {group.Describe()}..."); - StopCrashWatcher(group); - var workflow = pluginTools.CreateWorkflow(); - foreach (var c in group.Containers) + foreach (var node in group) { - workflow.Stop(c, waitTillStopped); + node.Stop(waitTillStopped); } Log("Stopped."); } @@ -157,13 +155,5 @@ namespace CodexPlugin { pluginTools.GetLog().Log(message); } - - private void StopCrashWatcher(CodexNodeGroup group) - { - foreach (var node in group) - { - node.CrashWatcher.Stop(); - } - } } } diff --git a/Tests/CodexContinuousTests/SingleTestRun.cs b/Tests/CodexContinuousTests/SingleTestRun.cs index e9afb313..8b77b775 100644 --- a/Tests/CodexContinuousTests/SingleTestRun.cs +++ b/Tests/CodexContinuousTests/SingleTestRun.cs @@ -204,7 +204,6 @@ namespace ContinuousTests result.Add("testname", testName); result.Add("message", message); result.Add("involvedpods", string.Join(",", nodes.Select(n => n.GetName()))); - result.Add("involvedpodnames", string.Join(",", nodes.Select(n => n.GetPodInfo().Name))); var error = message.Split(Environment.NewLine).First(); if (error.Contains(":")) error = error.Substring(1 + error.LastIndexOf(":")); diff --git a/Tests/ExperimentalTests/Helpers/PeerConnectionTestHelpers.cs b/Tests/ExperimentalTests/Helpers/PeerConnectionTestHelpers.cs index f0261a7f..692162b8 100644 --- a/Tests/ExperimentalTests/Helpers/PeerConnectionTestHelpers.cs +++ b/Tests/ExperimentalTests/Helpers/PeerConnectionTestHelpers.cs @@ -57,12 +57,7 @@ namespace CodexTests.Helpers { var peer = allEntries.SingleOrDefault(e => e.Response.Table.LocalNode.PeerId == node.PeerId); if (peer == null) return $"peerId: {node.PeerId} is not known."; - - var container = peer.Node.Container; - var podInfo = peer.Node.GetPodInfo(); - var ip = podInfo.Ip; - var discPort = container.Recipe.GetPortByTag(CodexContainerRecipe.DiscoveryPortTag)!; - return $"{ip}:{discPort.Number}"; + return peer.Node.GetDiscoveryEndpoint().ToString(); } } } diff --git a/Tools/CodexNetDeployer/Deployer.cs b/Tools/CodexNetDeployer/Deployer.cs index 1b8a4fe3..8ecc2434 100644 --- a/Tools/CodexNetDeployer/Deployer.cs +++ b/Tools/CodexNetDeployer/Deployer.cs @@ -205,10 +205,8 @@ namespace CodexNetDeployer Log("Starting container crash check..."); foreach (var startResult in startResults) { - var watcher = startResult.CodexNode.CrashWatcher; - if (watcher == null) - throw new Exception("Expected each CodexNode container to be created with a crash-watcher."); - if (watcher.HasContainerCrashed()) crashes.Add(startResult.CodexNode.Container); + var hasCrashed = startResult.CodexNode.HasCrashed(); + if (hasCrashed) crashes.Add(startResult.CodexNode.Container); } if (!crashes.Any()) From e45ed0c21e3fab5af4dd09b9cda82ac5cf287d57 Mon Sep 17 00:00:00 2001 From: ThatBen Date: Wed, 15 Jan 2025 15:00:25 +0100 Subject: [PATCH 02/25] pushes container concepts out of codexAccess --- ...ashWatcher.cs => ContainerCrashWatcher.cs} | 7 +- Framework/KubernetesWorkflow/K8sController.cs | 4 +- .../KubernetesWorkflow/StartupWorkflow.cs | 6 +- Framework/Utils/Address.cs | 4 +- Framework/Utils/CrashWatcher.cs | 9 +++ ProjectPlugins/CodexPlugin/CodexAccess.cs | 68 +++++++++------- ProjectPlugins/CodexPlugin/CodexInstance.cs | 14 ++++ ProjectPlugins/CodexPlugin/CodexNode.cs | 79 ++++++++----------- .../CodexPlugin/CodexNodeFactory.cs | 4 +- ProjectPlugins/CodexPlugin/CodexNodeGroup.cs | 36 +++------ ProjectPlugins/CodexPlugin/CodexStarter.cs | 2 +- .../MetricsPlugin/CoreInterfaceExtensions.cs | 7 +- ProjectPlugins/MetricsPlugin/MetricsAccess.cs | 6 +- ProjectPlugins/MetricsPlugin/MetricsPlugin.cs | 5 +- ProjectPlugins/MetricsPlugin/MetricsQuery.cs | 19 ++--- .../MetricsPlugin/MetricsScrapeTarget.cs | 24 +----- .../MetricsPlugin/PrometheusStarter.cs | 18 ++--- .../ScalabilityTests/ScalabilityTests.cs | 6 +- 18 files changed, 161 insertions(+), 157 deletions(-) rename Framework/KubernetesWorkflow/{CrashWatcher.cs => ContainerCrashWatcher.cs} (91%) create mode 100644 Framework/Utils/CrashWatcher.cs create mode 100644 ProjectPlugins/CodexPlugin/CodexInstance.cs diff --git a/Framework/KubernetesWorkflow/CrashWatcher.cs b/Framework/KubernetesWorkflow/ContainerCrashWatcher.cs similarity index 91% rename from Framework/KubernetesWorkflow/CrashWatcher.cs rename to Framework/KubernetesWorkflow/ContainerCrashWatcher.cs index 1692d920..1cb9cde8 100644 --- a/Framework/KubernetesWorkflow/CrashWatcher.cs +++ b/Framework/KubernetesWorkflow/ContainerCrashWatcher.cs @@ -1,9 +1,10 @@ using k8s; using Logging; +using Utils; namespace KubernetesWorkflow { - public class CrashWatcher + public class ContainerCrashWatcher : ICrashWatcher { private readonly ILog log; private readonly KubernetesClientConfiguration config; @@ -15,7 +16,7 @@ namespace KubernetesWorkflow private Task? worker; private Exception? workerException; - public CrashWatcher(ILog log, KubernetesClientConfiguration config, string containerName, string podName, string recipeName, string k8sNamespace) + public ContainerCrashWatcher(ILog log, KubernetesClientConfiguration config, string containerName, string podName, string recipeName, string k8sNamespace) { this.log = log; this.config = config; @@ -45,7 +46,7 @@ namespace KubernetesWorkflow if (workerException != null) throw new Exception("Exception occurred in CrashWatcher worker thread.", workerException); } - public bool HasContainerCrashed() + public bool HasCrashed() { using var client = new Kubernetes(config); var result = HasContainerBeenRestarted(client); diff --git a/Framework/KubernetesWorkflow/K8sController.cs b/Framework/KubernetesWorkflow/K8sController.cs index 1c8374c9..fb4f2fb9 100644 --- a/Framework/KubernetesWorkflow/K8sController.cs +++ b/Framework/KubernetesWorkflow/K8sController.cs @@ -946,13 +946,13 @@ namespace KubernetesWorkflow #endregion - public CrashWatcher CreateCrashWatcher(RunningContainer container) + public ContainerCrashWatcher CreateCrashWatcher(RunningContainer container) { var containerName = container.Name; var podName = GetPodName(container); var recipeName = container.Recipe.Name; - return new CrashWatcher(log, cluster.GetK8sClientConfig(), containerName, podName, recipeName, K8sNamespace); + return new ContainerCrashWatcher(log, cluster.GetK8sClientConfig(), containerName, podName, recipeName, K8sNamespace); } private V1Pod[] FindPodsByLabel(string podLabel) diff --git a/Framework/KubernetesWorkflow/StartupWorkflow.cs b/Framework/KubernetesWorkflow/StartupWorkflow.cs index 5c67d54a..d22dadee 100644 --- a/Framework/KubernetesWorkflow/StartupWorkflow.cs +++ b/Framework/KubernetesWorkflow/StartupWorkflow.cs @@ -13,7 +13,7 @@ namespace KubernetesWorkflow FutureContainers Start(int numberOfContainers, ILocation location, ContainerRecipeFactory recipeFactory, StartupConfig startupConfig); PodInfo GetPodInfo(RunningContainer container); PodInfo GetPodInfo(RunningPod pod); - CrashWatcher CreateCrashWatcher(RunningContainer container); + ContainerCrashWatcher CreateCrashWatcher(RunningContainer container); void Stop(RunningPod pod, bool waitTillStopped); void DownloadContainerLog(RunningContainer container, ILogHandler logHandler, int? tailLines = null, bool? previous = null); IDownloadedLog DownloadContainerLog(RunningContainer container, int? tailLines = null, bool? previous = null); @@ -93,7 +93,7 @@ namespace KubernetesWorkflow return K8s(c => c.GetPodInfo(pod.StartResult.Deployment)); } - public CrashWatcher CreateCrashWatcher(RunningContainer container) + public ContainerCrashWatcher CreateCrashWatcher(RunningContainer container) { return K8s(c => c.CreateCrashWatcher(container)); } @@ -209,6 +209,7 @@ namespace KubernetesWorkflow var port = startResult.GetExternalServicePorts(recipe, tag); return new Address( + logName: $"{recipe.Name}:{tag}", startResult.Cluster.HostAddress, port.Number); } @@ -220,6 +221,7 @@ namespace KubernetesWorkflow var port = startResult.GetInternalServicePorts(recipe, tag); return new Address( + logName: $"{serviceName}:{tag}", $"http://{serviceName}.{namespaceName}.svc.cluster.local", port.Number); } diff --git a/Framework/Utils/Address.cs b/Framework/Utils/Address.cs index 4d370836..58b8f2ac 100644 --- a/Framework/Utils/Address.cs +++ b/Framework/Utils/Address.cs @@ -2,12 +2,14 @@ { public class Address { - public Address(string host, int port) + public Address(string logName, string host, int port) { + LogName = logName; Host = host; Port = port; } + public string LogName { get; } public string Host { get; } public int Port { get; } diff --git a/Framework/Utils/CrashWatcher.cs b/Framework/Utils/CrashWatcher.cs new file mode 100644 index 00000000..2be46ab9 --- /dev/null +++ b/Framework/Utils/CrashWatcher.cs @@ -0,0 +1,9 @@ +namespace Utils +{ + public interface ICrashWatcher + { + void Start(); + void Stop(); + bool HasCrashed(); + } +} diff --git a/ProjectPlugins/CodexPlugin/CodexAccess.cs b/ProjectPlugins/CodexPlugin/CodexAccess.cs index 5d593758..d42194a8 100644 --- a/ProjectPlugins/CodexPlugin/CodexAccess.cs +++ b/ProjectPlugins/CodexPlugin/CodexAccess.cs @@ -1,7 +1,5 @@ using CodexOpenApi; using Core; -using KubernetesWorkflow; -using KubernetesWorkflow.Types; using Logging; using Newtonsoft.Json; using Utils; @@ -12,20 +10,30 @@ namespace CodexPlugin { private readonly ILog log; private readonly IPluginTools tools; + private readonly ICodexInstance instance; private readonly Mapper mapper = new Mapper(); - public CodexAccess(IPluginTools tools, RunningPod container, CrashWatcher crashWatcher) + public CodexAccess(IPluginTools tools, ICodexInstance instance, ICrashWatcher crashWatcher) { this.tools = tools; + this.instance = instance; log = tools.GetLog(); - Container = container; CrashWatcher = crashWatcher; CrashWatcher.Start(); } - public RunningPod Container { get; } - public CrashWatcher CrashWatcher { get; } + public ICrashWatcher CrashWatcher { get; } + + public string GetImageName() + { + return instance.ImageName; + } + + public DateTime GetStartUtc() + { + return instance.StartUtc; + } public DebugInfo GetDebugInfo() { @@ -170,29 +178,35 @@ namespace CodexPlugin public string GetName() { - return Container.Name; + return instance.Name; } - public PodInfo GetPodInfo() + public Address GetDiscoveryEndpoint() { - var workflow = tools.CreateWorkflow(); - return workflow.GetPodInfo(Container); + return instance.DiscoveryEndpoint; + //var info = codexAccess.GetPodInfo(); + //return new Address( + // logName: $"{GetName()}:DiscoveryPort", + // host: info.Ip, + // port: Container.Recipe.GetPortByTag(CodexContainerRecipe.DiscoveryPortTag)!.Number + //); } - public void DeleteRepoFolder() + 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); - } + //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(); } private T OnCodex(Func> action) @@ -223,7 +237,7 @@ namespace CodexPlugin } finally { - CrashWatcher.HasContainerCrashed(); + CrashWatcher.HasCrashed(); } } @@ -231,12 +245,12 @@ namespace CodexPlugin { return tools .CreateHttp(GetHttpId(), CheckContainerCrashed) - .CreateEndpoint(GetAddress(), "/api/codex/v1/", Container.Name); + .CreateEndpoint(GetAddress(), "/api/codex/v1/", GetName()); } private Address GetAddress() { - return Container.Containers.Single().GetAddress(CodexContainerRecipe.ApiPortTag); + return instance.ApiEndpoint; } private string GetHttpId() @@ -246,7 +260,7 @@ namespace CodexPlugin private void CheckContainerCrashed(HttpClient client) { - if (CrashWatcher.HasContainerCrashed()) throw new Exception($"Container {GetName()} has crashed."); + if (CrashWatcher.HasCrashed()) throw new Exception($"Container {GetName()} has crashed."); } private Retry CreateRetryConfig(string description, Action onFailure) diff --git a/ProjectPlugins/CodexPlugin/CodexInstance.cs b/ProjectPlugins/CodexPlugin/CodexInstance.cs new file mode 100644 index 00000000..c3a64ad2 --- /dev/null +++ b/ProjectPlugins/CodexPlugin/CodexInstance.cs @@ -0,0 +1,14 @@ +using Utils; + +namespace CodexPlugin +{ + public interface ICodexInstance + { + string Name { get; } + string ImageName { get; } + DateTime StartUtc { get; } + Address DiscoveryEndpoint { get; } + Address ApiEndpoint { get; } + void DeleteDataDirFolder(); + } +} diff --git a/ProjectPlugins/CodexPlugin/CodexNode.cs b/ProjectPlugins/CodexPlugin/CodexNode.cs index 94404404..01866b02 100644 --- a/ProjectPlugins/CodexPlugin/CodexNode.cs +++ b/ProjectPlugins/CodexPlugin/CodexNode.cs @@ -2,15 +2,13 @@ using Core; using FileUtils; using GethPlugin; -using KubernetesWorkflow; -using KubernetesWorkflow.Types; using Logging; using MetricsPlugin; using Utils; namespace CodexPlugin { - public interface ICodexNode : IHasContainer, IHasMetricsScrapeTarget, IHasEthAddress + public interface ICodexNode : IHasMetricsScrapeTarget, IHasEthAddress { string GetName(); string GetPeerId(); @@ -43,7 +41,7 @@ namespace CodexPlugin /// Warning! The node is not usable after this. /// TODO: Replace with delete-blocks debug call once available in Codex. /// - void DeleteRepoFolder(); + void DeleteDataDirFolder(); void Stop(bool waitTillStopped); bool HasCrashed(); } @@ -58,12 +56,13 @@ namespace CodexPlugin 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) { this.tools = tools; this.ethAccount = ethAccount; - CodexAccess = codexAccess; + this.codexAccess = codexAccess; Group = group; Marketplace = marketplaceAccess; this.hooks = hooks; @@ -75,7 +74,7 @@ namespace CodexPlugin public void Awake() { - hooks.OnNodeStarting(Container.Recipe.RecipeCreatedUtc, Container.Recipe.Image, ethAccount); + hooks.OnNodeStarting(codexAccess.GetStartUtc(), codexAccess.GetImageName(), ethAccount); } public void Initialize() @@ -83,21 +82,17 @@ namespace CodexPlugin hooks.OnNodeStarted(peerId, nodeId); } - public RunningPod Pod { get { return CodexAccess.Container; } } - - public RunningContainer Container { get { return Pod.Containers.Single(); } } - public CodexAccess CodexAccess { get; } - public CrashWatcher CrashWatcher { get => CodexAccess.CrashWatcher; } public CodexNodeGroup Group { get; } public IMarketplaceAccess Marketplace { get; } public DebugInfoVersion Version { get; private set; } public ITransferSpeeds TransferSpeeds { get => transferSpeeds; } - public IMetricsScrapeTarget MetricsScrapeTarget + public Address MetricsScrapeTarget { get { - return new MetricsScrapeTarget(CodexAccess.Container.Containers.First(), CodexContainerRecipe.MetricsPortTag); + throw new Exception("todo"); + //return new MetricsScrapeTarget(CodexAccess.Container.Containers.First(), CodexContainerRecipe.MetricsPortTag); } } @@ -121,7 +116,7 @@ namespace CodexPlugin public string GetName() { - return Container.Name; + return codexAccess.GetName(); } public string GetPeerId() @@ -131,7 +126,7 @@ namespace CodexPlugin public DebugInfo GetDebugInfo(bool log = false) { - var debugInfo = CodexAccess.GetDebugInfo(); + var debugInfo = codexAccess.GetDebugInfo(); if (log) { var known = string.Join(",", debugInfo.Table.Nodes.Select(n => n.PeerId)); @@ -142,12 +137,12 @@ namespace CodexPlugin public string GetSpr() { - return CodexAccess.GetSpr(); + return codexAccess.GetSpr(); } public DebugPeer GetDebugPeer(string peerId) { - return CodexAccess.GetDebugPeer(peerId); + return codexAccess.GetDebugPeer(peerId); } public ContentId UploadFile(TrackedFile file) @@ -172,7 +167,7 @@ namespace CodexPlugin var logMessage = $"Uploading file {file.Describe()} with contentType: '{input.ContentType}' and disposition: '{input.ContentDisposition}'..."; var measurement = Stopwatch.Measure(log, logMessage, () => { - return CodexAccess.UploadFile(input, onFailure); + return codexAccess.UploadFile(input, onFailure); }); var response = measurement.Value; @@ -212,7 +207,7 @@ namespace CodexPlugin public LocalDataset DownloadStreamless(ContentId cid) { Log($"Downloading streamless '{cid}' (no-wait)"); - return CodexAccess.DownloadStreamless(cid); + return codexAccess.DownloadStreamless(cid); } public LocalDataset DownloadStreamlessWait(ContentId cid, ByteSize size) @@ -222,7 +217,7 @@ namespace CodexPlugin var sw = Stopwatch.Measure(log, nameof(DownloadStreamlessWait), () => { var startSpace = Space(); - var result = CodexAccess.DownloadStreamless(cid); + var result = codexAccess.DownloadStreamless(cid); WaitUntilQuotaUsedIncreased(startSpace, size); return result; }); @@ -233,17 +228,17 @@ namespace CodexPlugin public LocalDataset DownloadManifestOnly(ContentId cid) { Log($"Downloading manifest-only '{cid}'"); - return CodexAccess.DownloadManifestOnly(cid); + return codexAccess.DownloadManifestOnly(cid); } public LocalDatasetList LocalFiles() { - return CodexAccess.LocalFiles(); + return codexAccess.LocalFiles(); } public CodexSpace Space() { - return CodexAccess.Space(); + return codexAccess.Space(); } public void ConnectToPeer(ICodexNode node) @@ -252,31 +247,30 @@ namespace CodexPlugin Log($"Connecting to peer {peer.GetName()}..."); var peerInfo = node.GetDebugInfo(); - CodexAccess.ConnectToPeer(peerInfo.Id, GetPeerMultiAddresses(peer, peerInfo)); + codexAccess.ConnectToPeer(peerInfo.Id, GetPeerMultiAddresses(peer, peerInfo)); Log($"Successfully connected to peer {peer.GetName()}."); } - public void DeleteRepoFolder() + public void DeleteDataDirFolder() { - CodexAccess.DeleteRepoFolder(); + codexAccess.DeleteDataDirFolder(); } public void Stop(bool waitTillStopped) { Log("Stopping..."); hooks.OnNodeStopping(); - - CrashWatcher.Stop(); + codexAccess.CrashWatcher.Stop(); Group.Stop(this, waitTillStopped); } public void EnsureOnlineGetVersionResponse() { - var debugInfo = Time.Retry(CodexAccess.GetDebugInfo, "ensure online"); + var debugInfo = Time.Retry(codexAccess.GetDebugInfo, "ensure online"); peerId = debugInfo.Id; nodeId = debugInfo.Table.LocalNode.NodeId; - var nodeName = CodexAccess.Container.Name; + var nodeName = codexAccess.Container.Name; if (!debugInfo.Version.IsValid()) { @@ -292,16 +286,12 @@ namespace CodexPlugin public Address GetDiscoveryEndpoint() { - var info = CodexAccess.GetPodInfo(); - return new Address( - host: info.Ip, - port: Container.Recipe.GetPortByTag(CodexContainerRecipe.DiscoveryPortTag)!.Number - ); + return codexAccess.GetDiscoveryEndpoint(); } public bool HasCrashed() { - return CrashWatcher.HasContainerCrashed(); + return codexAccess.CrashWatcher.HasCrashed(); } public override string ToString() @@ -311,13 +301,12 @@ namespace CodexPlugin private string[] GetPeerMultiAddresses(CodexNode peer, DebugInfo peerInfo) { - // The peer we want to connect is in a different pod. - // We must replace the default IP with the pod IP in the multiAddress. - var workflow = tools.CreateWorkflow(); - var podInfo = workflow.GetPodInfo(peer.Pod); + var peerId = peer.GetDiscoveryEndpoint().Host + .Replace("http://", "") + .Replace("https://", ""); return peerInfo.Addrs.Select(a => a - .Replace("0.0.0.0", podInfo.Ip)) + .Replace("0.0.0.0", peerId)) .ToArray(); } @@ -330,11 +319,11 @@ namespace CodexPlugin // Type of stream generated by openAPI client does not support timeouts. var start = DateTime.UtcNow; var cts = new CancellationTokenSource(); - var downloadTask = Task.Run(() => + var downloadTask = Task.Run((Action)(() => { - using var downloadStream = CodexAccess.DownloadFile(contentId, onFailure); - downloadStream.CopyTo(fileStream); - }, cts.Token); + using var downloadStream = this.codexAccess.DownloadFile(contentId, onFailure); + downloadStream.CopyTo((Stream)fileStream); + }), cts.Token); while (DateTime.UtcNow - start < timeout) { diff --git a/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs b/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs index f55180c6..c4646f78 100644 --- a/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs +++ b/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs @@ -9,7 +9,7 @@ namespace CodexPlugin public interface ICodexNodeFactory { CodexNode CreateOnlineCodexNode(CodexAccess access, CodexNodeGroup group); - CrashWatcher CreateCrashWatcher(RunningContainer c); + ContainerCrashWatcher CreateCrashWatcher(RunningContainer c); } public class CodexNodeFactory : ICodexNodeFactory @@ -45,7 +45,7 @@ namespace CodexPlugin return ethAccount; } - public CrashWatcher CreateCrashWatcher(RunningContainer c) + public ContainerCrashWatcher CreateCrashWatcher(RunningContainer c) { return tools.CreateWorkflow().CreateCrashWatcher(c); } diff --git a/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs b/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs index e4b999b8..0f8353eb 100644 --- a/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs +++ b/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs @@ -1,7 +1,7 @@ using Core; -using KubernetesWorkflow.Types; using MetricsPlugin; using System.Collections; +using Utils; namespace CodexPlugin { @@ -14,12 +14,12 @@ namespace CodexPlugin public class CodexNodeGroup : ICodexNodeGroup { private readonly CodexStarter starter; + private CodexNode[] nodes; - public CodexNodeGroup(CodexStarter starter, IPluginTools tools, RunningPod[] containers, ICodexNodeFactory codexNodeFactory) + public CodexNodeGroup(CodexStarter starter, IPluginTools tools, CodexNode[] nodes) { this.starter = starter; - Containers = containers; - Nodes = containers.Select(c => CreateOnlineCodexNode(c, tools, codexNodeFactory)).ToArray(); + this.nodes = nodes; Version = new DebugInfoVersion(); } @@ -35,21 +35,18 @@ namespace CodexPlugin { starter.BringOffline(this, waitTillStopped); // Clear everything. Prevent accidental use. - Nodes = Array.Empty(); - Containers = null!; + nodes = Array.Empty(); } public void Stop(CodexNode node, bool waitTillStopped) { - starter.Stop(node.Pod, waitTillStopped); - Nodes = Nodes.Where(n => n != node).ToArray(); - Containers = Containers.Where(c => c != node.Pod).ToArray(); + starter.Stop(node, waitTillStopped); + nodes = nodes.Where(n => n != node).ToArray(); } - public RunningPod[] Containers { get; private set; } - public CodexNode[] Nodes { get; private set; } + public ICodexNode[] Nodes => nodes; public DebugInfoVersion Version { get; private set; } - public IMetricsScrapeTarget[] ScrapeTargets => Nodes.Select(n => n.MetricsScrapeTarget).ToArray(); + public Address[] ScrapeTargets => Nodes.Select(n => n.MetricsScrapeTarget).ToArray(); public IEnumerator GetEnumerator() { @@ -63,12 +60,12 @@ namespace CodexPlugin public string Describe() { - return $"group:[{Containers.Describe()}]"; + return $"group:[{string.Join(",", Nodes.Select(n => n.GetName()))}]"; } public void EnsureOnline() { - foreach (var node in Nodes) node.EnsureOnlineGetVersionResponse(); + foreach (var node in nodes) node.EnsureOnlineGetVersionResponse(); var versionResponses = Nodes.Select(n => n.Version); var first = versionResponses.First(); @@ -79,16 +76,7 @@ namespace CodexPlugin } Version = first; - foreach (var node in Nodes) node.Initialize(); - } - - private CodexNode CreateOnlineCodexNode(RunningPod c, IPluginTools tools, ICodexNodeFactory factory) - { - var watcher = factory.CreateCrashWatcher(c.Containers.Single()); - var access = new CodexAccess(tools, c, watcher); - var node = factory.CreateOnlineCodexNode(access, this); - node.Awake(); - return node; + foreach (var node in nodes) node.Initialize(); } } } diff --git a/ProjectPlugins/CodexPlugin/CodexStarter.cs b/ProjectPlugins/CodexPlugin/CodexStarter.cs index 1aebc72e..1945d68f 100644 --- a/ProjectPlugins/CodexPlugin/CodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/CodexStarter.cs @@ -68,7 +68,7 @@ namespace CodexPlugin Log("Stopped."); } - public void Stop(RunningPod pod, bool waitTillStopped) + public void Stop(CodexNode pod, bool waitTillStopped) { Log($"Stopping node..."); var workflow = pluginTools.CreateWorkflow(); diff --git a/ProjectPlugins/MetricsPlugin/CoreInterfaceExtensions.cs b/ProjectPlugins/MetricsPlugin/CoreInterfaceExtensions.cs index c8075a02..e632e288 100644 --- a/ProjectPlugins/MetricsPlugin/CoreInterfaceExtensions.cs +++ b/ProjectPlugins/MetricsPlugin/CoreInterfaceExtensions.cs @@ -1,6 +1,7 @@ using Core; using KubernetesWorkflow.Types; using Logging; +using Utils; namespace MetricsPlugin { @@ -11,7 +12,7 @@ namespace MetricsPlugin return Plugin(ci).DeployMetricsCollector(scrapeTargets.Select(t => t.MetricsScrapeTarget).ToArray(), scrapeInterval); } - public static RunningPod DeployMetricsCollector(this CoreInterface ci, TimeSpan scrapeInterval, params IMetricsScrapeTarget[] scrapeTargets) + public static RunningPod DeployMetricsCollector(this CoreInterface ci, TimeSpan scrapeInterval, params Address[] scrapeTargets) { return Plugin(ci).DeployMetricsCollector(scrapeTargets, scrapeInterval); } @@ -21,7 +22,7 @@ namespace MetricsPlugin return ci.WrapMetricsCollector(metricsPod, scrapeTarget.MetricsScrapeTarget); } - public static IMetricsAccess WrapMetricsCollector(this CoreInterface ci, RunningPod metricsPod, IMetricsScrapeTarget scrapeTarget) + public static IMetricsAccess WrapMetricsCollector(this CoreInterface ci, RunningPod metricsPod, Address scrapeTarget) { return Plugin(ci).WrapMetricsCollectorDeployment(metricsPod, scrapeTarget); } @@ -36,7 +37,7 @@ namespace MetricsPlugin return ci.GetMetricsFor(scrapeInterval, scrapeTargets.Select(t => t.MetricsScrapeTarget).ToArray()); } - public static IMetricsAccess[] GetMetricsFor(this CoreInterface ci, TimeSpan scrapeInterval, params IMetricsScrapeTarget[] scrapeTargets) + public static IMetricsAccess[] GetMetricsFor(this CoreInterface ci, TimeSpan scrapeInterval, params Address[] scrapeTargets) { var rc = ci.DeployMetricsCollector(scrapeInterval, scrapeTargets); return scrapeTargets.Select(t => ci.WrapMetricsCollector(rc, t)).ToArray(); diff --git a/ProjectPlugins/MetricsPlugin/MetricsAccess.cs b/ProjectPlugins/MetricsPlugin/MetricsAccess.cs index 74b8d91d..704f035e 100644 --- a/ProjectPlugins/MetricsPlugin/MetricsAccess.cs +++ b/ProjectPlugins/MetricsPlugin/MetricsAccess.cs @@ -15,13 +15,13 @@ namespace MetricsPlugin public class MetricsAccess : IMetricsAccess { private readonly MetricsQuery query; - private readonly IMetricsScrapeTarget target; + private readonly Address target; - public MetricsAccess(MetricsQuery query, IMetricsScrapeTarget target) + public MetricsAccess(MetricsQuery query, Address target) { this.query = query; this.target = target; - TargetName = target.Container.Name; + TargetName = $"'{target.Host}'"; } public string TargetName { get; } diff --git a/ProjectPlugins/MetricsPlugin/MetricsPlugin.cs b/ProjectPlugins/MetricsPlugin/MetricsPlugin.cs index 01f6e70e..a796ba06 100644 --- a/ProjectPlugins/MetricsPlugin/MetricsPlugin.cs +++ b/ProjectPlugins/MetricsPlugin/MetricsPlugin.cs @@ -1,6 +1,7 @@ using Core; using KubernetesWorkflow.Types; using Logging; +using Utils; namespace MetricsPlugin { @@ -31,12 +32,12 @@ namespace MetricsPlugin { } - public RunningPod DeployMetricsCollector(IMetricsScrapeTarget[] scrapeTargets, TimeSpan scrapeInterval) + public RunningPod DeployMetricsCollector(Address[] scrapeTargets, TimeSpan scrapeInterval) { return starter.CollectMetricsFor(scrapeTargets, scrapeInterval); } - public IMetricsAccess WrapMetricsCollectorDeployment(RunningPod runningPod, IMetricsScrapeTarget target) + public IMetricsAccess WrapMetricsCollectorDeployment(RunningPod runningPod, Address target) { runningPod = SerializeGate.Gate(runningPod); return starter.CreateAccessForTarget(runningPod, target); diff --git a/ProjectPlugins/MetricsPlugin/MetricsQuery.cs b/ProjectPlugins/MetricsPlugin/MetricsQuery.cs index 9b110b74..7b70bd5c 100644 --- a/ProjectPlugins/MetricsPlugin/MetricsQuery.cs +++ b/ProjectPlugins/MetricsPlugin/MetricsQuery.cs @@ -3,6 +3,7 @@ using IdentityModel; using KubernetesWorkflow.Types; using Logging; using System.Globalization; +using Utils; namespace MetricsPlugin { @@ -23,7 +24,7 @@ namespace MetricsPlugin public RunningContainer RunningContainer { get; } - public Metrics GetMostRecent(string metricName, IMetricsScrapeTarget target) + public Metrics GetMostRecent(string metricName, Address target) { var response = GetLastOverTime(metricName, GetInstanceStringForNode(target)); if (response == null) throw new Exception($"Failed to get most recent metric: {metricName}"); @@ -53,7 +54,7 @@ namespace MetricsPlugin return result; } - public Metrics GetAllMetricsForNode(IMetricsScrapeTarget target) + public Metrics GetAllMetricsForNode(Address target) { var instanceString = GetInstanceStringForNode(target); var response = endpoint.HttpGetJson($"query?query={instanceString}{GetQueryTimeRange()}"); @@ -139,12 +140,12 @@ namespace MetricsPlugin }; } - private string GetInstanceNameForNode(IMetricsScrapeTarget target) + private string GetInstanceNameForNode(Address target) { - return ScrapeTargetHelper.FormatTarget(log, target); + return ScrapeTargetHelper.FormatTarget(target); } - private string GetInstanceStringForNode(IMetricsScrapeTarget target) + private string GetInstanceStringForNode(Address target) { return "{instance=\"" + GetInstanceNameForNode(target) + "\"}"; } @@ -172,9 +173,9 @@ namespace MetricsPlugin return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(unixSeconds); } - private void Log(IMetricsScrapeTarget target, string metricName, Metrics result) + private void Log(Address target, string metricName, Metrics result) { - Log($"{target.Container.Name} '{metricName}' = {result}"); + Log($"{target.LogName} '{metricName}' = {result}"); } private void Log(string metricName, Metrics result) @@ -182,9 +183,9 @@ namespace MetricsPlugin Log($"'{metricName}' = {result}"); } - private void Log(IMetricsScrapeTarget target, Metrics result) + private void Log(Address target, Metrics result) { - Log($"{target.Container.Name} => {result}"); + Log($"{target.LogName} => {result}"); } private void Log(string msg) diff --git a/ProjectPlugins/MetricsPlugin/MetricsScrapeTarget.cs b/ProjectPlugins/MetricsPlugin/MetricsScrapeTarget.cs index 5c9cbd14..5f497de4 100644 --- a/ProjectPlugins/MetricsPlugin/MetricsScrapeTarget.cs +++ b/ProjectPlugins/MetricsPlugin/MetricsScrapeTarget.cs @@ -1,32 +1,14 @@ -using KubernetesWorkflow.Types; +using Utils; namespace MetricsPlugin { - public interface IMetricsScrapeTarget - { - RunningContainer Container { get; } - string MetricsPortTag { get; } - } - public interface IHasMetricsScrapeTarget { - IMetricsScrapeTarget MetricsScrapeTarget { get; } + Address MetricsScrapeTarget { get; } } public interface IHasManyMetricScrapeTargets { - IMetricsScrapeTarget[] ScrapeTargets { get; } - } - - public class MetricsScrapeTarget : IMetricsScrapeTarget - { - public MetricsScrapeTarget(RunningContainer container, string metricsPortTag) - { - Container = container; - MetricsPortTag = metricsPortTag; - } - - public RunningContainer Container { get; } - public string MetricsPortTag { get; } + Address[] ScrapeTargets { get; } } } diff --git a/ProjectPlugins/MetricsPlugin/PrometheusStarter.cs b/ProjectPlugins/MetricsPlugin/PrometheusStarter.cs index bae5ef6e..7a70c776 100644 --- a/ProjectPlugins/MetricsPlugin/PrometheusStarter.cs +++ b/ProjectPlugins/MetricsPlugin/PrometheusStarter.cs @@ -3,6 +3,7 @@ using KubernetesWorkflow; using KubernetesWorkflow.Types; using Logging; using System.Text; +using Utils; namespace MetricsPlugin { @@ -16,7 +17,7 @@ namespace MetricsPlugin this.tools = tools; } - public RunningPod CollectMetricsFor(IMetricsScrapeTarget[] targets, TimeSpan scrapeInterval) + public RunningPod CollectMetricsFor(Address[] targets, TimeSpan scrapeInterval) { if (!targets.Any()) throw new ArgumentException(nameof(targets) + " must not be empty."); @@ -32,7 +33,7 @@ namespace MetricsPlugin return runningContainers; } - public MetricsAccess CreateAccessForTarget(RunningPod metricsPod, IMetricsScrapeTarget target) + public MetricsAccess CreateAccessForTarget(RunningPod metricsPod, Address target) { var metricsQuery = new MetricsQuery(tools, metricsPod.Containers.Single()); return new MetricsAccess(metricsQuery, target); @@ -48,7 +49,7 @@ namespace MetricsPlugin tools.GetLog().Log(msg); } - private string GeneratePrometheusConfig(IMetricsScrapeTarget[] targets, TimeSpan scrapeInterval) + private string GeneratePrometheusConfig(Address[] targets, TimeSpan scrapeInterval) { var secs = Convert.ToInt32(scrapeInterval.TotalSeconds); if (secs < 1) throw new Exception("ScrapeInterval can't be < 1s"); @@ -74,19 +75,18 @@ namespace MetricsPlugin return Convert.ToBase64String(bytes); } - private string FormatTarget(IMetricsScrapeTarget target) + private string FormatTarget(Address target) { - return ScrapeTargetHelper.FormatTarget(tools.GetLog(), target); + return ScrapeTargetHelper.FormatTarget(target); } } public static class ScrapeTargetHelper { - public static string FormatTarget(ILog log, IMetricsScrapeTarget target) + public static string FormatTarget(Address target) { - var a = target.Container.GetAddress(target.MetricsPortTag); - var host = a.Host.Replace("http://", "").Replace("https://", ""); - return $"{host}:{a.Port}"; + var host = target.Host.Replace("http://", "").Replace("https://", ""); + return $"{host}:{target.Port}"; } } } diff --git a/Tests/CodexLongTests/ScalabilityTests/ScalabilityTests.cs b/Tests/CodexLongTests/ScalabilityTests/ScalabilityTests.cs index c3b05f7a..a3f05970 100644 --- a/Tests/CodexLongTests/ScalabilityTests/ScalabilityTests.cs +++ b/Tests/CodexLongTests/ScalabilityTests/ScalabilityTests.cs @@ -47,7 +47,7 @@ public class ScalabilityTests : CodexDistTest downloadedFile!.AssertIsEqual(testFile); - uploader.DeleteRepoFolder(); + uploader.DeleteDataDirFolder(); uploader.Stop(true); var otherDownloader = nodes.PickOneRandom(); @@ -55,8 +55,8 @@ public class ScalabilityTests : CodexDistTest downloadedFile!.AssertIsEqual(testFile); - downloader.DeleteRepoFolder(); - otherDownloader.DeleteRepoFolder(); + downloader.DeleteDataDirFolder(); + otherDownloader.DeleteDataDirFolder(); } /// From ee0193c8798f25d3462b7b1d34283f850a231f01 Mon Sep 17 00:00:00 2001 From: ThatBen Date: Wed, 15 Jan 2025 15:43:50 +0100 Subject: [PATCH 03/25] wiring up started --- ProjectPlugins/CodexPlugin/CodexAccess.cs | 37 ++++++---- ProjectPlugins/CodexPlugin/CodexInstance.cs | 68 ++++++++++++++++++- ProjectPlugins/CodexPlugin/CodexNode.cs | 67 +++++++++--------- .../CodexPlugin/CodexNodeFactory.cs | 32 ++------- ProjectPlugins/CodexPlugin/CodexNodeGroup.cs | 20 ++---- ProjectPlugins/CodexPlugin/CodexStarter.cs | 57 ++++++++++------ ProjectPlugins/CodexPlugin/ProcessControl.cs | 7 ++ 7 files changed, 183 insertions(+), 105 deletions(-) create mode 100644 ProjectPlugins/CodexPlugin/ProcessControl.cs diff --git a/ProjectPlugins/CodexPlugin/CodexAccess.cs b/ProjectPlugins/CodexPlugin/CodexAccess.cs index d42194a8..8271344b 100644 --- a/ProjectPlugins/CodexPlugin/CodexAccess.cs +++ b/ProjectPlugins/CodexPlugin/CodexAccess.cs @@ -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(); } diff --git a/ProjectPlugins/CodexPlugin/CodexInstance.cs b/ProjectPlugins/CodexPlugin/CodexInstance.cs index c3a64ad2..8c0c41ba 100644 --- a/ProjectPlugins/CodexPlugin/CodexInstance.cs +++ b/ProjectPlugins/CodexPlugin/CodexInstance.cs @@ -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().MetricsEnabled) + { + metricsAddress = container.GetAddress(CodexContainerRecipe.MetricsPortTag); + } + ethAccount = container.Recipe.Additionals.Get(); + } + + 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; + } } } diff --git a/ProjectPlugins/CodexPlugin/CodexNode.cs b/ProjectPlugins/CodexPlugin/CodexNode.cs index 01866b02..9c3e6fa3 100644 --- a/ProjectPlugins/CodexPlugin/CodexNode.cs +++ b/ProjectPlugins/CodexPlugin/CodexNode.cs @@ -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) diff --git a/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs b/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs index c4646f78..719b0282 100644 --- a/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs +++ b/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs @@ -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(); - if (ethAccount == null) return null; - return ethAccount; - } - - public ContainerCrashWatcher CreateCrashWatcher(RunningContainer c) - { - return tools.CreateWorkflow().CreateCrashWatcher(c); - } } } diff --git a/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs b/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs index 0f8353eb..be90581a 100644 --- a/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs +++ b/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs @@ -7,18 +7,16 @@ namespace CodexPlugin { public interface ICodexNodeGroup : IEnumerable, 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(); + 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(); } } } diff --git a/ProjectPlugins/CodexPlugin/CodexStarter.cs b/ProjectPlugins/CodexPlugin/CodexStarter.cs index 1945d68f..30037a04 100644 --- a/ProjectPlugins/CodexPlugin/CodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/CodexStarter.cs @@ -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 podMap = new Dictionary(); 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"); diff --git a/ProjectPlugins/CodexPlugin/ProcessControl.cs b/ProjectPlugins/CodexPlugin/ProcessControl.cs new file mode 100644 index 00000000..b0faed11 --- /dev/null +++ b/ProjectPlugins/CodexPlugin/ProcessControl.cs @@ -0,0 +1,7 @@ +namespace CodexPlugin +{ + public interface IProcessControl + { + void Stop(ICodexInstance instance, bool waitTillStopped); + } +} From 0aac2d8e98180aeddf2bb16f6a88d96410a2f92d Mon Sep 17 00:00:00 2001 From: ThatBen Date: Wed, 15 Jan 2025 16:05:57 +0100 Subject: [PATCH 04/25] wired up except for container log access --- ProjectPlugins/CodexPlugin/CodexAccess.cs | 12 +++++++++++- ProjectPlugins/CodexPlugin/CodexInstance.cs | 3 +++ ProjectPlugins/CodexPlugin/CodexNode.cs | 18 ++++++++++++++++++ .../CodexPlugin/MarketplaceAccess.cs | 2 +- Tests/CodexContinuousTests/ContinuousTest.cs | 3 ++- .../ElasticSearchLogDownloader.cs | 2 +- Tests/CodexContinuousTests/NodeRunner.cs | 2 +- Tests/CodexContinuousTests/SingleTestRun.cs | 2 +- Tests/CodexContinuousTests/StartupChecker.cs | 4 ++-- .../DataTests/InterruptUploadTest.cs | 2 +- .../MarketTests/ContractFailedTest.cs | 2 +- .../NodeTests/BasicInfoTests.cs | 2 +- Tools/AutoClient/Program.cs | 1 + Tools/BiblioTech/CodexCidChecker.cs | 1 + Tools/CodexNetDeployer/Deployer.cs | 6 +++--- 15 files changed, 48 insertions(+), 14 deletions(-) diff --git a/ProjectPlugins/CodexPlugin/CodexAccess.cs b/ProjectPlugins/CodexPlugin/CodexAccess.cs index 8271344b..4981dc10 100644 --- a/ProjectPlugins/CodexPlugin/CodexAccess.cs +++ b/ProjectPlugins/CodexPlugin/CodexAccess.cs @@ -31,7 +31,7 @@ namespace CodexPlugin public void Stop(bool waitTillStopped) { CrashWatcher.Stop(); - processControl.Stop(instance); + processControl.Stop(instance, waitTillStopped); // Prevents accidental use after stop: instance = null!; } @@ -203,6 +203,16 @@ namespace CodexPlugin //); } + public Address GetApiEndpoint() + { + return instance.ApiEndpoint; + } + + public Address GetListenEndpoint() + { + return instance.ListenEndpoint; + } + public Address? GetMetricsEndpoint() { return instance.GetMetricsEndpoint(); diff --git a/ProjectPlugins/CodexPlugin/CodexInstance.cs b/ProjectPlugins/CodexPlugin/CodexInstance.cs index 8c0c41ba..9f7a1a19 100644 --- a/ProjectPlugins/CodexPlugin/CodexInstance.cs +++ b/ProjectPlugins/CodexPlugin/CodexInstance.cs @@ -13,6 +13,7 @@ namespace CodexPlugin DateTime StartUtc { get; } Address DiscoveryEndpoint { get; } Address ApiEndpoint { get; } + Address ListenEndpoint { get; } void DeleteDataDirFolder(); EthAccount? GetEthAccount(); Address? GetMetricsEndpoint(); @@ -37,6 +38,7 @@ namespace CodexPlugin DiscoveryEndpoint = container.GetAddress(CodexContainerRecipe.DiscoveryPortTag); ApiEndpoint = container.GetAddress(CodexContainerRecipe.ApiPortTag); + ListenEndpoint = container.GetAddress(CodexContainerRecipe.ListenPortTag); if (pod.StartupConfig.Get().MetricsEnabled) { @@ -50,6 +52,7 @@ namespace CodexPlugin public DateTime StartUtc { get; } public Address DiscoveryEndpoint { get; } public Address ApiEndpoint { get; } + public Address ListenEndpoint { get; } public void DeleteDataDirFolder() { diff --git a/ProjectPlugins/CodexPlugin/CodexNode.cs b/ProjectPlugins/CodexPlugin/CodexNode.cs index 9c3e6fa3..ab72dc5e 100644 --- a/ProjectPlugins/CodexPlugin/CodexNode.cs +++ b/ProjectPlugins/CodexPlugin/CodexNode.cs @@ -11,6 +11,7 @@ namespace CodexPlugin public interface ICodexNode : IHasMetricsScrapeTarget, IHasEthAddress { string GetName(); + string GetImageName(); string GetPeerId(); DebugInfo GetDebugInfo(bool log = false); string GetSpr(); @@ -36,6 +37,8 @@ namespace CodexPlugin EthAccount EthAccount { get; } Address GetDiscoveryEndpoint(); + Address GetApiEndpoint(); + Address GetListenEndpoint(); /// /// Warning! The node is not usable after this. @@ -119,6 +122,11 @@ namespace CodexPlugin return codexAccess.GetName(); } + public string GetImageName() + { + return codexAccess.GetImageName(); + } + public string GetPeerId() { return peerId; @@ -269,6 +277,16 @@ namespace CodexPlugin return codexAccess.GetDiscoveryEndpoint(); } + public Address GetApiEndpoint() + { + return codexAccess.GetApiEndpoint(); + } + + public Address GetListenEndpoint() + { + return codexAccess.GetListenEndpoint(); + } + public bool HasCrashed() { return codexAccess.CrashWatcher.HasCrashed(); diff --git a/ProjectPlugins/CodexPlugin/MarketplaceAccess.cs b/ProjectPlugins/CodexPlugin/MarketplaceAccess.cs index a657e9b0..34720df7 100644 --- a/ProjectPlugins/CodexPlugin/MarketplaceAccess.cs +++ b/ProjectPlugins/CodexPlugin/MarketplaceAccess.cs @@ -72,7 +72,7 @@ namespace CodexPlugin private void Log(string msg) { - log.Log($"{codexAccess.Container.Containers.Single().Name} {msg}"); + log.Log($"{codexAccess.GetName()} {msg}"); } } diff --git a/Tests/CodexContinuousTests/ContinuousTest.cs b/Tests/CodexContinuousTests/ContinuousTest.cs index 5b746682..00a4c3a7 100644 --- a/Tests/CodexContinuousTests/ContinuousTest.cs +++ b/Tests/CodexContinuousTests/ContinuousTest.cs @@ -4,6 +4,7 @@ using DistTestCore; using FileUtils; using Logging; using MetricsPlugin; +using Utils; namespace ContinuousTests { @@ -53,7 +54,7 @@ namespace ContinuousTests return CreateMetricsAccess(target.MetricsScrapeTarget); } - public IMetricsAccess CreateMetricsAccess(IMetricsScrapeTarget target) + public IMetricsAccess CreateMetricsAccess(Address target) { if (Configuration.CodexDeployment.PrometheusContainer == null) throw new Exception("Expected prometheus to be part of Codex deployment."); diff --git a/Tests/CodexContinuousTests/ElasticSearchLogDownloader.cs b/Tests/CodexContinuousTests/ElasticSearchLogDownloader.cs index 397b5db1..ecd646dd 100644 --- a/Tests/CodexContinuousTests/ElasticSearchLogDownloader.cs +++ b/Tests/CodexContinuousTests/ElasticSearchLogDownloader.cs @@ -68,7 +68,7 @@ namespace ContinuousTests { var serviceName = "elasticsearch"; var k8sNamespace = "monitoring"; - var address = new Address($"http://{serviceName}.{k8sNamespace}.svc.cluster.local", 9200); + var address = new Address("ElasticSearchEndpoint", $"http://{serviceName}.{k8sNamespace}.svc.cluster.local", 9200); var baseUrl = ""; var http = tools.CreateHttp(address.ToString(), client => diff --git a/Tests/CodexContinuousTests/NodeRunner.cs b/Tests/CodexContinuousTests/NodeRunner.cs index 77a383e1..b69792fe 100644 --- a/Tests/CodexContinuousTests/NodeRunner.cs +++ b/Tests/CodexContinuousTests/NodeRunner.cs @@ -40,7 +40,7 @@ namespace ContinuousTests var entryPoint = CreateEntryPoint(); // We have to be sure that the transient node we start is using the same image as whatever's already in the deployed network. // Therefore, we use the image of the bootstrap node. - CodexContainerRecipe.DockerImageOverride = bootstrapNode.Container.Recipe.Image; + CodexContainerRecipe.DockerImageOverride = bootstrapNode.GetImageName(); try { diff --git a/Tests/CodexContinuousTests/SingleTestRun.cs b/Tests/CodexContinuousTests/SingleTestRun.cs index 8b77b775..a8a798ad 100644 --- a/Tests/CodexContinuousTests/SingleTestRun.cs +++ b/Tests/CodexContinuousTests/SingleTestRun.cs @@ -285,7 +285,7 @@ namespace ContinuousTests private string GetContainerNames() { if (handle.Test.RequiredNumberOfNodes == -1) return "(All Nodes)"; - return $"({string.Join(",", nodes.Select(n => n.Container.Name))})"; + return $"({string.Join(",", nodes.Select(n => n.GetName()))})"; } private ICodexNode[] CreateRandomNodes() diff --git a/Tests/CodexContinuousTests/StartupChecker.cs b/Tests/CodexContinuousTests/StartupChecker.cs index ca4c8874..94f2d978 100644 --- a/Tests/CodexContinuousTests/StartupChecker.cs +++ b/Tests/CodexContinuousTests/StartupChecker.cs @@ -98,8 +98,8 @@ namespace ContinuousTests { cancelToken.ThrowIfCancellationRequested(); - var address = n.Container.GetAddress(CodexContainerRecipe.ApiPortTag); - log.Log($"Checking {n.Container.Name} @ '{address}'..."); + var address = n.GetApiEndpoint(); + log.Log($"Checking {n.GetName()} @ '{address}'..."); if (EnsureOnline(log, n)) { diff --git a/Tests/CodexReleaseTests/DataTests/InterruptUploadTest.cs b/Tests/CodexReleaseTests/DataTests/InterruptUploadTest.cs index ae9c9d58..ad1f6c64 100644 --- a/Tests/CodexReleaseTests/DataTests/InterruptUploadTest.cs +++ b/Tests/CodexReleaseTests/DataTests/InterruptUploadTest.cs @@ -38,7 +38,7 @@ namespace CodexReleaseTests.DataTests private Process StartCurlUploadProcess(ICodexNode node, TrackedFile file) { - var apiAddress = node.Container.GetAddress(CodexContainerRecipe.ApiPortTag); + var apiAddress = node.GetApiEndpoint(); var codexUrl = $"{apiAddress}/api/codex/v1/data"; var filePath = file.Filename; return Process.Start("curl", $"-X POST {codexUrl} -H \"Content-Type: application/octet-stream\" -T {filePath}"); diff --git a/Tests/CodexReleaseTests/MarketTests/ContractFailedTest.cs b/Tests/CodexReleaseTests/MarketTests/ContractFailedTest.cs index 6a0f9c51..dd79c6af 100644 --- a/Tests/CodexReleaseTests/MarketTests/ContractFailedTest.cs +++ b/Tests/CodexReleaseTests/MarketTests/ContractFailedTest.cs @@ -36,7 +36,7 @@ namespace CodexReleaseTests.MarketTests request.WaitForStorageContractStarted(); AssertContractSlotsAreFilledByHosts(request, hosts); - hosts.BringOffline(waitTillStopped: true); + hosts.Stop(waitTillStopped: true); WaitForSlotFreedEvents(); diff --git a/Tests/CodexReleaseTests/NodeTests/BasicInfoTests.cs b/Tests/CodexReleaseTests/NodeTests/BasicInfoTests.cs index 914bedd7..9b9f4bbf 100644 --- a/Tests/CodexReleaseTests/NodeTests/BasicInfoTests.cs +++ b/Tests/CodexReleaseTests/NodeTests/BasicInfoTests.cs @@ -51,7 +51,7 @@ namespace CodexReleaseTests.NodeTests public void AnnounceAddress() { var node = StartCodex(); - var addr = node.Container.GetInternalAddress(CodexContainerRecipe.ListenPortTag); + var addr = node.GetListenEndpoint(); var info = node.GetDebugInfo(); diff --git a/Tools/AutoClient/Program.cs b/Tools/AutoClient/Program.cs index e22c4198..8c8aed13 100644 --- a/Tools/AutoClient/Program.cs +++ b/Tools/AutoClient/Program.cs @@ -94,6 +94,7 @@ public class Program var port = Convert.ToInt32(endpoint.Substring(splitIndex + 1)); var address = new Address( + logName: $"cdx@{host}:{port}", host: host, port: port ); diff --git a/Tools/BiblioTech/CodexCidChecker.cs b/Tools/BiblioTech/CodexCidChecker.cs index ca2723cf..3b4b8bb9 100644 --- a/Tools/BiblioTech/CodexCidChecker.cs +++ b/Tools/BiblioTech/CodexCidChecker.cs @@ -172,6 +172,7 @@ namespace BiblioTech var port = Convert.ToInt32(endpoint.Substring(splitIndex + 1)); var address = new Address( + logName: $"cdx@{host}:{port}", host: host, port: port ); diff --git a/Tools/CodexNetDeployer/Deployer.cs b/Tools/CodexNetDeployer/Deployer.cs index 8ecc2434..e746f9ea 100644 --- a/Tools/CodexNetDeployer/Deployer.cs +++ b/Tools/CodexNetDeployer/Deployer.cs @@ -201,12 +201,12 @@ namespace CodexNetDeployer private void CheckContainerRestarts(List startResults) { - var crashes = new List(); + var crashes = new List(); Log("Starting container crash check..."); foreach (var startResult in startResults) { var hasCrashed = startResult.CodexNode.HasCrashed(); - if (hasCrashed) crashes.Add(startResult.CodexNode.Container); + if (hasCrashed) crashes.Add(startResult.CodexNode); } if (!crashes.Any()) @@ -216,7 +216,7 @@ namespace CodexNetDeployer else { Log( - $"Check failed. The following containers have crashed: {string.Join(",", crashes.Select(c => c.Name))}"); + $"Check failed. The following containers have crashed: {string.Join(",", crashes.Select(c => c.GetName()))}"); throw new Exception("Deployment failed: One or more containers crashed."); } } From 0c7b8fb33e86997f5e384859e1d2a269790ea916 Mon Sep 17 00:00:00 2001 From: ThatBen Date: Wed, 15 Jan 2025 16:24:36 +0100 Subject: [PATCH 05/25] debugs rewiring --- ProjectPlugins/CodexPlugin/CodexInstance.cs | 19 ++++++++++--------- .../DataTests/TwoClientTest.cs | 5 ----- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/ProjectPlugins/CodexPlugin/CodexInstance.cs b/ProjectPlugins/CodexPlugin/CodexInstance.cs index 9f7a1a19..53bb2de3 100644 --- a/ProjectPlugins/CodexPlugin/CodexInstance.cs +++ b/ProjectPlugins/CodexPlugin/CodexInstance.cs @@ -24,7 +24,6 @@ namespace CodexPlugin 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) @@ -36,14 +35,9 @@ namespace CodexPlugin ImageName = container.Recipe.Image; StartUtc = container.Recipe.RecipeCreatedUtc; - DiscoveryEndpoint = container.GetAddress(CodexContainerRecipe.DiscoveryPortTag); + DiscoveryEndpoint = container.GetInternalAddress(CodexContainerRecipe.DiscoveryPortTag); ApiEndpoint = container.GetAddress(CodexContainerRecipe.ApiPortTag); - ListenEndpoint = container.GetAddress(CodexContainerRecipe.ListenPortTag); - - if (pod.StartupConfig.Get().MetricsEnabled) - { - metricsAddress = container.GetAddress(CodexContainerRecipe.MetricsPortTag); - } + ListenEndpoint = container.GetInternalAddress(CodexContainerRecipe.ListenPortTag); ethAccount = container.Recipe.Additionals.Get(); } @@ -77,7 +71,14 @@ namespace CodexPlugin public Address? GetMetricsEndpoint() { - return metricsAddress; + try + { + return container.GetInternalAddress(CodexContainerRecipe.MetricsPortTag); + } + catch + { + return null; + } } } } diff --git a/Tests/CodexReleaseTests/DataTests/TwoClientTest.cs b/Tests/CodexReleaseTests/DataTests/TwoClientTest.cs index afb6d07a..676653e7 100644 --- a/Tests/CodexReleaseTests/DataTests/TwoClientTest.cs +++ b/Tests/CodexReleaseTests/DataTests/TwoClientTest.cs @@ -1,11 +1,6 @@ using CodexPlugin; using CodexTests; using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Utils; namespace CodexReleaseTests.DataTests From ec644eed4ada4836091bc5bb4e6251f3681455b0 Mon Sep 17 00:00:00 2001 From: ThatBen Date: Thu, 16 Jan 2025 10:15:02 +0100 Subject: [PATCH 06/25] Moving downloadedlog to logging module --- Framework/Core/CoreInterface.cs | 1 + Framework/KubernetesWorkflow/LogHandler.cs | 2 +- .../KubernetesWorkflow/StartupWorkflow.cs | 2 +- Framework/Logging/BaseLog.cs | 6 +- .../DownloadedLog.cs | 28 ++++--- Framework/Logging/LogFile.cs | 22 +----- ProjectPlugins/CodexPlugin/CodexAccess.cs | 17 ++-- ProjectPlugins/CodexPlugin/CodexDeployment.cs | 20 ++--- ProjectPlugins/CodexPlugin/CodexInstance.cs | 77 +++++++++---------- ProjectPlugins/CodexPlugin/CodexNode.cs | 6 ++ ProjectPlugins/CodexPlugin/CodexStarter.cs | 28 ++++++- .../OverwatchSupport/CodexLogConverter.cs | 1 + ProjectPlugins/CodexPlugin/ProcessControl.cs | 24 +++++- .../MetricsPlugin/MetricsDownloader.cs | 2 +- .../ElasticSearchLogDownloader.cs | 2 +- Tests/CodexContinuousTests/NodeRunner.cs | 8 +- Tests/CodexContinuousTests/SingleTestRun.cs | 15 ++-- .../MultiPeerDownloadTests.cs | 4 +- .../DataTests/InterruptUploadTest.cs | 2 +- Tests/DistTestCore/DownloadedLogExtensions.cs | 2 +- Tests/DistTestCore/Logs/BaseTestLog.cs | 1 - Tests/DistTestCore/TestLifecycle.cs | 1 + .../BasicTests/ExampleTests.cs | 2 +- Tests/ExperimentalTests/CodexDistTest.cs | 8 +- .../UtilityTests/LogHelperTests.cs | 2 +- Tools/CodexNetDeployer/CodexNodeStarter.cs | 2 +- Tools/CodexNetDeployer/Deployer.cs | 3 +- 27 files changed, 164 insertions(+), 124 deletions(-) rename Framework/{KubernetesWorkflow => Logging}/DownloadedLog.cs (72%) diff --git a/Framework/Core/CoreInterface.cs b/Framework/Core/CoreInterface.cs index f3682930..1db1101c 100644 --- a/Framework/Core/CoreInterface.cs +++ b/Framework/Core/CoreInterface.cs @@ -1,5 +1,6 @@ using KubernetesWorkflow; using KubernetesWorkflow.Types; +using Logging; namespace Core { diff --git a/Framework/KubernetesWorkflow/LogHandler.cs b/Framework/KubernetesWorkflow/LogHandler.cs index 5d3c07d8..af77ef75 100644 --- a/Framework/KubernetesWorkflow/LogHandler.cs +++ b/Framework/KubernetesWorkflow/LogHandler.cs @@ -29,7 +29,7 @@ namespace KubernetesWorkflow { LogFile = sourceLog.CreateSubfile(addFileName); - var msg = $"{description} -->> {LogFile.FullFilename}"; + var msg = $"{description} -->> {LogFile.Filename}"; sourceLog.Log(msg); LogFile.Write(msg); diff --git a/Framework/KubernetesWorkflow/StartupWorkflow.cs b/Framework/KubernetesWorkflow/StartupWorkflow.cs index d22dadee..67d01def 100644 --- a/Framework/KubernetesWorkflow/StartupWorkflow.cs +++ b/Framework/KubernetesWorkflow/StartupWorkflow.cs @@ -134,7 +134,7 @@ namespace KubernetesWorkflow controller.DownloadPodLog(container, logHandler, tailLines, previous); }); - return new DownloadedLog(logHandler, container.Name); + return new DownloadedLog(logHandler.LogFile, container.Name); } public string ExecuteCommand(RunningContainer container, string command, params string[] args) diff --git a/Framework/Logging/BaseLog.cs b/Framework/Logging/BaseLog.cs index 72907dbb..2c79fd39 100644 --- a/Framework/Logging/BaseLog.cs +++ b/Framework/Logging/BaseLog.cs @@ -34,7 +34,7 @@ namespace Logging { get { - if (logFile == null) logFile = new LogFile(GetFullName(), "log"); + if (logFile == null) logFile = new LogFile(GetFullName() + ".log"); return logFile; } } @@ -69,7 +69,7 @@ namespace Logging public virtual void Delete() { - File.Delete(LogFile.FullFilename); + File.Delete(LogFile.Filename); } public LogFile CreateSubfile(string addName, string ext = "log") @@ -78,7 +78,7 @@ namespace Logging .Replace("<", "") .Replace(">", ""); - return new LogFile($"{GetFullName()}_{GetSubfileNumber()}_{addName}", ext); + return new LogFile($"{GetFullName()}_{GetSubfileNumber()}_{addName}.{ext}"); } protected string ApplyReplacements(string str) diff --git a/Framework/KubernetesWorkflow/DownloadedLog.cs b/Framework/Logging/DownloadedLog.cs similarity index 72% rename from Framework/KubernetesWorkflow/DownloadedLog.cs rename to Framework/Logging/DownloadedLog.cs index edc62079..a9eb398f 100644 --- a/Framework/KubernetesWorkflow/DownloadedLog.cs +++ b/Framework/Logging/DownloadedLog.cs @@ -1,10 +1,8 @@ -using Logging; - -namespace KubernetesWorkflow +namespace Logging { public interface IDownloadedLog { - string ContainerName { get; } + string SourceName { get; } void IterateLines(Action action); void IterateLines(Action action, params string[] thatContain); @@ -14,21 +12,27 @@ namespace KubernetesWorkflow void DeleteFile(); } - internal class DownloadedLog : IDownloadedLog + public class DownloadedLog : IDownloadedLog { private readonly LogFile logFile; - internal DownloadedLog(WriteToFileLogHandler logHandler, string containerName) + public DownloadedLog(string filepath, string sourceName) { - logFile = logHandler.LogFile; - ContainerName = containerName; + logFile = new LogFile(filepath); + SourceName = sourceName; } - public string ContainerName { get; } + public DownloadedLog(LogFile logFile, string sourceName) + { + this.logFile = logFile; + SourceName = sourceName; + } + + public string SourceName { get; } public void IterateLines(Action action) { - using var file = File.OpenRead(logFile.FullFilename); + using var file = File.OpenRead(logFile.Filename); using var streamReader = new StreamReader(file); var line = streamReader.ReadLine(); @@ -64,12 +68,12 @@ namespace KubernetesWorkflow public string GetFilepath() { - return logFile.FullFilename; + return logFile.Filename; } public void DeleteFile() { - File.Delete(logFile.FullFilename); + File.Delete(logFile.Filename); } } } diff --git a/Framework/Logging/LogFile.cs b/Framework/Logging/LogFile.cs index 1019f08a..f9147ce9 100644 --- a/Framework/Logging/LogFile.cs +++ b/Framework/Logging/LogFile.cs @@ -4,20 +4,16 @@ namespace Logging { public class LogFile { - private readonly string extension; private readonly object fileLock = new object(); - private string filename; - public LogFile(string filename, string extension) + public LogFile(string filename) { - this.filename = filename; - this.extension = extension; - FullFilename = filename + "." + extension; + Filename = filename; EnsurePathExists(filename); } - public string FullFilename { get; private set; } + public string Filename { get; private set; } public void Write(string message) { @@ -30,7 +26,7 @@ namespace Logging { lock (fileLock) { - File.AppendAllLines(FullFilename, new[] { message }); + File.AppendAllLines(Filename, new[] { message }); } } catch (Exception ex) @@ -39,16 +35,6 @@ namespace Logging } } - public void ConcatToFilename(string toAdd) - { - var oldFullName = FullFilename; - - filename += toAdd; - FullFilename = filename + "." + extension; - - File.Move(oldFullName, FullFilename); - } - private static string GetTimestamp() { return $"[{Time.FormatTimestamp(DateTime.UtcNow)}]"; diff --git a/ProjectPlugins/CodexPlugin/CodexAccess.cs b/ProjectPlugins/CodexPlugin/CodexAccess.cs index 4981dc10..227956e5 100644 --- a/ProjectPlugins/CodexPlugin/CodexAccess.cs +++ b/ProjectPlugins/CodexPlugin/CodexAccess.cs @@ -36,6 +36,11 @@ namespace CodexPlugin instance = null!; } + public IDownloadedLog DownloadLog(string additionalName = "") + { + return processControl.DownloadLog(instance, log.CreateSubfile(GetName() + additionalName)); + } + public string GetImageName() { return instance.ImageName; @@ -195,12 +200,6 @@ namespace CodexPlugin public Address GetDiscoveryEndpoint() { return instance.DiscoveryEndpoint; - //var info = codexAccess.GetPodInfo(); - //return new Address( - // logName: $"{GetName()}:DiscoveryPort", - // host: info.Ip, - // port: Container.Recipe.GetPortByTag(CodexContainerRecipe.DiscoveryPortTag)!.Number - //); } public Address GetApiEndpoint() @@ -215,17 +214,17 @@ namespace CodexPlugin public Address? GetMetricsEndpoint() { - return instance.GetMetricsEndpoint(); + return instance.MetricsEndpoint; } public EthAccount? GetEthAccount() { - return instance.GetEthAccount(); + return instance.EthAccount; } public void DeleteDataDirFolder() { - instance.DeleteDataDirFolder(); + processControl.DeleteDataDirFolder(instance); } private T OnCodex(Func> action) diff --git a/ProjectPlugins/CodexPlugin/CodexDeployment.cs b/ProjectPlugins/CodexPlugin/CodexDeployment.cs index 7cbf4506..8ea5c9ad 100644 --- a/ProjectPlugins/CodexPlugin/CodexDeployment.cs +++ b/ProjectPlugins/CodexPlugin/CodexDeployment.cs @@ -29,17 +29,17 @@ namespace CodexPlugin public DeploymentMetadata Metadata { get; } } - public class CodexInstance - { - public CodexInstance(RunningPod pod, DebugInfo info) - { - Pod = pod; - Info = info; - } + //public class CodexInstance + //{ + // public CodexInstance(RunningPod pod, DebugInfo info) + // { + // Pod = pod; + // Info = info; + // } - public RunningPod Pod { get; } - public DebugInfo Info { get; } - } + // public RunningPod Pod { get; } + // public DebugInfo Info { get; } + //} public class DeploymentMetadata { diff --git a/ProjectPlugins/CodexPlugin/CodexInstance.cs b/ProjectPlugins/CodexPlugin/CodexInstance.cs index 53bb2de3..15cde305 100644 --- a/ProjectPlugins/CodexPlugin/CodexInstance.cs +++ b/ProjectPlugins/CodexPlugin/CodexInstance.cs @@ -14,31 +14,22 @@ namespace CodexPlugin Address DiscoveryEndpoint { get; } Address ApiEndpoint { get; } Address ListenEndpoint { get; } - void DeleteDataDirFolder(); - EthAccount? GetEthAccount(); - Address? GetMetricsEndpoint(); + EthAccount? EthAccount { get; } + Address? MetricsEndpoint { get; } } - public class CodexContainerInstance : ICodexInstance + public class CodexInstance : ICodexInstance { - private readonly RunningContainer container; - private readonly IPluginTools tools; - private readonly ILog log; - private readonly EthAccount? ethAccount = null; - - public CodexContainerInstance(IPluginTools tools, ILog log, RunningPod pod) + public CodexInstance(string name, string imageName, DateTime startUtc, Address discoveryEndpoint, Address apiEndpoint, Address listenEndpoint, EthAccount? ethAccount, Address? metricsEndpoint) { - container = pod.Containers.Single(); - this.tools = tools; - this.log = log; - Name = container.Name; - ImageName = container.Recipe.Image; - StartUtc = container.Recipe.RecipeCreatedUtc; - - DiscoveryEndpoint = container.GetInternalAddress(CodexContainerRecipe.DiscoveryPortTag); - ApiEndpoint = container.GetAddress(CodexContainerRecipe.ApiPortTag); - ListenEndpoint = container.GetInternalAddress(CodexContainerRecipe.ListenPortTag); - ethAccount = container.Recipe.Additionals.Get(); + Name = name; + ImageName = imageName; + StartUtc = startUtc; + DiscoveryEndpoint = discoveryEndpoint; + ApiEndpoint = apiEndpoint; + ListenEndpoint = listenEndpoint; + EthAccount = ethAccount; + MetricsEndpoint = metricsEndpoint; } public string Name { get; } @@ -47,29 +38,37 @@ namespace CodexPlugin public Address DiscoveryEndpoint { get; } public Address ApiEndpoint { get; } public Address ListenEndpoint { get; } + public EthAccount? EthAccount { get; } + public Address? MetricsEndpoint { get; } + } - public void DeleteDataDirFolder() + public static class CodexInstanceContainerExtension + { + public static ICodexInstance CreateFromPod(RunningPod pod) { - 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); - } + var container = pod.Containers.Single(); + + return new CodexInstance( + name: container.Name, + imageName: container.Recipe.Image, + startUtc: container.Recipe.RecipeCreatedUtc, + discoveryEndpoint: container.GetInternalAddress(CodexContainerRecipe.DiscoveryPortTag), + apiEndpoint: container.GetAddress(CodexContainerRecipe.ApiPortTag), + listenEndpoint: container.GetInternalAddress(CodexContainerRecipe.ListenPortTag), + ethAccount: container.Recipe.Additionals.Get(), + metricsEndpoint: GetMetricsEndpoint(container) + ); } - public EthAccount? GetEthAccount() - { - return ethAccount; - } + // todo: is this needed for the discovery address?? + //var info = codexAccess.GetPodInfo(); + //return new Address( + // logName: $"{GetName()}:DiscoveryPort", + // host: info.Ip, + // port: Container.Recipe.GetPortByTag(CodexContainerRecipe.DiscoveryPortTag)!.Number + //); - public Address? GetMetricsEndpoint() + private static Address? GetMetricsEndpoint(RunningContainer container) { try { diff --git a/ProjectPlugins/CodexPlugin/CodexNode.cs b/ProjectPlugins/CodexPlugin/CodexNode.cs index ab72dc5e..12cf0f28 100644 --- a/ProjectPlugins/CodexPlugin/CodexNode.cs +++ b/ProjectPlugins/CodexPlugin/CodexNode.cs @@ -46,6 +46,7 @@ namespace CodexPlugin /// void DeleteDataDirFolder(); void Stop(bool waitTillStopped); + IDownloadedLog DownloadLog(string additionalName = ""); bool HasCrashed(); } @@ -272,6 +273,11 @@ namespace CodexPlugin codexAccess.Stop(waitTillStopped); } + public IDownloadedLog DownloadLog(string additionalName = "") + { + return codexAccess.DownloadLog(additionalName); + } + public Address GetDiscoveryEndpoint() { return codexAccess.GetDiscoveryEndpoint(); diff --git a/ProjectPlugins/CodexPlugin/CodexStarter.cs b/ProjectPlugins/CodexPlugin/CodexStarter.cs index 30037a04..9dd44aa6 100644 --- a/ProjectPlugins/CodexPlugin/CodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/CodexStarter.cs @@ -59,6 +59,32 @@ namespace CodexPlugin Log("Stopped."); } + public IDownloadedLog DownloadLog(ICodexInstance instance, LogFile file) + { + var workflow = pluginTools.CreateWorkflow(); + var pod = podMap[instance]; + return workflow.DownloadContainerLog(pod.Containers.Single()); + } + + public void DeleteDataDirFolder(ICodexInstance instance) + { + var pod = podMap[instance]; + var container = pod.Containers.Single(); + + try + { + var dataDirVar = container.Recipe.EnvVars.Single(e => e.Name == "CODEX_DATA_DIR"); + var dataDir = dataDirVar.Value; + var workflow = pluginTools.CreateWorkflow(); + workflow.ExecuteCommand(container, "rm", "-Rfv", $"/codex/{dataDir}/repo"); + Log("Deleted repo folder."); + } + catch (Exception e) + { + Log("Unable to delete repo folder: " + e); + } + } + public ICodexNodeGroup WrapCodexContainers(CoreInterface coreInterface, RunningPod[] containers) { var codexNodeFactory = new CodexNodeFactory(pluginTools, HooksFactory); @@ -145,7 +171,7 @@ namespace CodexPlugin private ICodexInstance CreateInstance(RunningPod pod) { - var instance = new CodexContainerInstance(pluginTools, pluginTools.GetLog(), pod); + var instance = CodexInstanceContainerExtension.CreateFromPod(pod); podMap.Add(instance, pod); return instance; } diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexLogConverter.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexLogConverter.cs index 860180e8..9e55e3a4 100644 --- a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexLogConverter.cs +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexLogConverter.cs @@ -1,5 +1,6 @@ using CodexPlugin.OverwatchSupport.LineConverters; using KubernetesWorkflow; +using Logging; using OverwatchTranscript; using Utils; diff --git a/ProjectPlugins/CodexPlugin/ProcessControl.cs b/ProjectPlugins/CodexPlugin/ProcessControl.cs index b0faed11..8b138e49 100644 --- a/ProjectPlugins/CodexPlugin/ProcessControl.cs +++ b/ProjectPlugins/CodexPlugin/ProcessControl.cs @@ -1,7 +1,29 @@ -namespace CodexPlugin +using Logging; + +namespace CodexPlugin { public interface IProcessControl { void Stop(ICodexInstance instance, bool waitTillStopped); + IDownloadedLog DownloadLog(ICodexInstance instance, LogFile file); + void DeleteDataDirFolder(ICodexInstance instance); } + + + //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); + // } + //} + } diff --git a/ProjectPlugins/MetricsPlugin/MetricsDownloader.cs b/ProjectPlugins/MetricsPlugin/MetricsDownloader.cs index 1c642a04..507b1ba7 100644 --- a/ProjectPlugins/MetricsPlugin/MetricsDownloader.cs +++ b/ProjectPlugins/MetricsPlugin/MetricsDownloader.cs @@ -26,7 +26,7 @@ namespace MetricsPlugin private LogFile WriteToFile(string nodeName, string[] headers, Dictionary> map) { var file = log.CreateSubfile("csv"); - log.Log($"Downloading metrics for {nodeName} to file {file.FullFilename}"); + log.Log($"Downloading metrics for {nodeName} to file {file.Filename}"); file.WriteRaw(string.Join(",", headers)); diff --git a/Tests/CodexContinuousTests/ElasticSearchLogDownloader.cs b/Tests/CodexContinuousTests/ElasticSearchLogDownloader.cs index ecd646dd..7d491939 100644 --- a/Tests/CodexContinuousTests/ElasticSearchLogDownloader.cs +++ b/Tests/CodexContinuousTests/ElasticSearchLogDownloader.cs @@ -37,7 +37,7 @@ namespace ContinuousTests var endpoint = CreateElasticSearchEndpoint(); var queryTemplate = CreateQueryTemplate(container, startUtc, endUtc); - targetFile.Write($"Downloading '{container.Name}' to '{targetFile.FullFilename}'."); + targetFile.Write($"Downloading '{container.Name}' to '{targetFile.Filename}'."); var reconstructor = new LogReconstructor(targetFile, endpoint, queryTemplate); reconstructor.DownloadFullLog(); diff --git a/Tests/CodexContinuousTests/NodeRunner.cs b/Tests/CodexContinuousTests/NodeRunner.cs index b69792fe..b51f6222 100644 --- a/Tests/CodexContinuousTests/NodeRunner.cs +++ b/Tests/CodexContinuousTests/NodeRunner.cs @@ -24,12 +24,6 @@ namespace ContinuousTests this.customNamespace = customNamespace; } - public IDownloadedLog DownloadLog(RunningContainer container, int? tailLines = null) - { - var entryPoint = CreateEntryPoint(); - return entryPoint.CreateInterface().DownloadLog(container, tailLines); - } - public void RunNode(Action setup, Action operation) { RunNode(nodes.ToList().PickOneRandom(), setup, operation); @@ -59,7 +53,7 @@ namespace ContinuousTests } catch { - DownloadLog(node.Container); + node.DownloadLog(); throw; } } diff --git a/Tests/CodexContinuousTests/SingleTestRun.cs b/Tests/CodexContinuousTests/SingleTestRun.cs index a8a798ad..8958c5b6 100644 --- a/Tests/CodexContinuousTests/SingleTestRun.cs +++ b/Tests/CodexContinuousTests/SingleTestRun.cs @@ -125,13 +125,14 @@ namespace ContinuousTests foreach (var node in nodes) { - var container = node.Container; - var deploymentName = container.RunningPod.StartResult.Deployment.Name; - var namespaceName = container.RunningPod.StartResult.Cluster.Configuration.KubernetesNamespace; - var openingLine = - $"{namespaceName} - {deploymentName} = {node.Container.Name} = {node.GetDebugInfo().Id}"; - elasticSearchLogDownloader.Download(fixtureLog.CreateSubfile(node.GetName()), node.Container, effectiveStart, - effectiveEnd, openingLine); + //var container = node.Container; + //var deploymentName = container.RunningPod.StartResult.Deployment.Name; + //var namespaceName = container.RunningPod.StartResult.Cluster.Configuration.KubernetesNamespace; + //var openingLine = + // $"{namespaceName} - {deploymentName} = {node.Container.Name} = {node.GetDebugInfo().Id}"; + //elasticSearchLogDownloader.Download(fixtureLog.CreateSubfile(node.GetName()), node.Container, effectiveStart, + // effectiveEnd, openingLine); + throw new NotImplementedException("access to container info is unavilable."); } } diff --git a/Tests/CodexLongTests/ScalabilityTests/MultiPeerDownloadTests.cs b/Tests/CodexLongTests/ScalabilityTests/MultiPeerDownloadTests.cs index 3d37c635..fabf51e0 100644 --- a/Tests/CodexLongTests/ScalabilityTests/MultiPeerDownloadTests.cs +++ b/Tests/CodexLongTests/ScalabilityTests/MultiPeerDownloadTests.cs @@ -31,7 +31,7 @@ namespace CodexTests.ScalabilityTests Task.WaitAll(uploadTasks); var cid = new ContentId(uploadTasks.Select(t => t.Result.Id).Distinct().Single()); - var uploadLog = Ci.DownloadLog(hosts[0]); + var uploadLog = hosts[0].DownloadLog(); var expectedNumberOfBlocks = RoundUp(fileSize.MB().SizeInBytes, 64.KB().SizeInBytes) + 1; // +1 for manifest block. var blockCids = uploadLog .FindLinesThatContain("Block Stored") @@ -50,7 +50,7 @@ namespace CodexTests.ScalabilityTests var resultFile = client.DownloadContent(cid); resultFile!.AssertIsEqual(file); - var downloadLog = Ci.DownloadLog(client); + var downloadLog = client.DownloadLog(); var host = string.Empty; var blockAddressHostMap = new Dictionary>(); downloadLog diff --git a/Tests/CodexReleaseTests/DataTests/InterruptUploadTest.cs b/Tests/CodexReleaseTests/DataTests/InterruptUploadTest.cs index ad1f6c64..06b2009c 100644 --- a/Tests/CodexReleaseTests/DataTests/InterruptUploadTest.cs +++ b/Tests/CodexReleaseTests/DataTests/InterruptUploadTest.cs @@ -32,7 +32,7 @@ namespace CodexReleaseTests.DataTests process.Kill(); Thread.Sleep(1000); - var log = Ci.DownloadLog(node); + var log = node.DownloadLog(); return !log.GetLinesContaining("Unhandled exception in async proc, aborting").Any(); } diff --git a/Tests/DistTestCore/DownloadedLogExtensions.cs b/Tests/DistTestCore/DownloadedLogExtensions.cs index 3d4cb84e..293a672e 100644 --- a/Tests/DistTestCore/DownloadedLogExtensions.cs +++ b/Tests/DistTestCore/DownloadedLogExtensions.cs @@ -1,4 +1,4 @@ -using KubernetesWorkflow; +using Logging; using NUnit.Framework; namespace DistTestCore diff --git a/Tests/DistTestCore/Logs/BaseTestLog.cs b/Tests/DistTestCore/Logs/BaseTestLog.cs index 4cd4aa65..22485545 100644 --- a/Tests/DistTestCore/Logs/BaseTestLog.cs +++ b/Tests/DistTestCore/Logs/BaseTestLog.cs @@ -23,7 +23,6 @@ namespace DistTestCore.Logs { if (hasFailed) return; hasFailed = true; - LogFile.ConcatToFilename("_FAILED"); } } } diff --git a/Tests/DistTestCore/TestLifecycle.cs b/Tests/DistTestCore/TestLifecycle.cs index 675be8c7..16a4ecd4 100644 --- a/Tests/DistTestCore/TestLifecycle.cs +++ b/Tests/DistTestCore/TestLifecycle.cs @@ -4,6 +4,7 @@ using FileUtils; using KubernetesWorkflow; using KubernetesWorkflow.Recipe; using KubernetesWorkflow.Types; +using Logging; using Utils; namespace DistTestCore diff --git a/Tests/ExperimentalTests/BasicTests/ExampleTests.cs b/Tests/ExperimentalTests/BasicTests/ExampleTests.cs index b2cb76f8..50367969 100644 --- a/Tests/ExperimentalTests/BasicTests/ExampleTests.cs +++ b/Tests/ExperimentalTests/BasicTests/ExampleTests.cs @@ -20,7 +20,7 @@ namespace CodexTests.BasicTests var localDatasets = primary.LocalFiles(); CollectionAssert.Contains(localDatasets.Content.Select(c => c.Cid), cid); - var log = Ci.DownloadLog(primary); + var log = primary.DownloadLog(); log.AssertLogContains("Uploaded file"); } diff --git a/Tests/ExperimentalTests/CodexDistTest.cs b/Tests/ExperimentalTests/CodexDistTest.cs index 0ceec87d..fbe25ffa 100644 --- a/Tests/ExperimentalTests/CodexDistTest.cs +++ b/Tests/ExperimentalTests/CodexDistTest.cs @@ -105,7 +105,7 @@ namespace CodexTests public void CheckLogForErrors(ICodexNode node) { Log($"Checking {node.GetName()} log for errors."); - var log = Ci.DownloadLog(node); + var log = node.DownloadLog(); log.AssertLogDoesNotContain("Block validation failed"); log.AssertLogDoesNotContainLinesStartingWith("ERR "); @@ -203,7 +203,7 @@ namespace CodexTests Stopwatch.Measure(lifecycle.Log, $"Transcript.Finalize: {outputFilepath}", () => { - writer.IncludeFile(lifecycle.Log.LogFile.FullFilename); + writer.IncludeFile(lifecycle.Log.LogFile.Filename); writer.Finalize(outputFilepath); }); } @@ -215,9 +215,9 @@ namespace CodexTests private string GetOutputFullPath(TestLifecycle lifecycle, CreateTranscriptAttribute attr) { - var outputPath = Path.GetDirectoryName(lifecycle.Log.LogFile.FullFilename); + var outputPath = Path.GetDirectoryName(lifecycle.Log.LogFile.Filename); if (outputPath == null) throw new Exception("Logfile path is null"); - var filename = Path.GetFileNameWithoutExtension(lifecycle.Log.LogFile.FullFilename); + var filename = Path.GetFileNameWithoutExtension(lifecycle.Log.LogFile.Filename); if (string.IsNullOrEmpty(filename)) throw new Exception("Logfile name is null or empty"); var outputFile = Path.Combine(outputPath, filename + "_" + attr.OutputFilename); if (!outputFile.EndsWith(".owts")) outputFile += ".owts"; diff --git a/Tests/ExperimentalTests/UtilityTests/LogHelperTests.cs b/Tests/ExperimentalTests/UtilityTests/LogHelperTests.cs index acc73d34..443201f4 100644 --- a/Tests/ExperimentalTests/UtilityTests/LogHelperTests.cs +++ b/Tests/ExperimentalTests/UtilityTests/LogHelperTests.cs @@ -35,7 +35,7 @@ namespace CodexTests.UtilityTests private Dictionary GetLogMap(ICodexNode node, DateTime? startUtc = null) { - var log = Ci.DownloadLog(node); + var log = node.DownloadLog(); var map = new Dictionary(); log.IterateLines(line => { diff --git a/Tools/CodexNetDeployer/CodexNodeStarter.cs b/Tools/CodexNetDeployer/CodexNodeStarter.cs index 81df4e0a..d970b016 100644 --- a/Tools/CodexNetDeployer/CodexNodeStarter.cs +++ b/Tools/CodexNetDeployer/CodexNodeStarter.cs @@ -100,7 +100,7 @@ namespace CodexNetDeployer if (codexNode != null) { Console.WriteLine("Downloading container log."); - ci.DownloadLog(codexNode); + codexNode.DownloadLog(); } return null; diff --git a/Tools/CodexNetDeployer/Deployer.cs b/Tools/CodexNetDeployer/Deployer.cs index e746f9ea..1981b81a 100644 --- a/Tools/CodexNetDeployer/Deployer.cs +++ b/Tools/CodexNetDeployer/Deployer.cs @@ -181,7 +181,8 @@ namespace CodexNetDeployer private CodexInstance CreateCodexInstance(ICodexNode node) { - return new CodexInstance(node.Container.RunningPod, node.GetDebugInfo()); + //return new CodexInstance(node.Container.RunningPod, node.GetDebugInfo()); + throw new NotImplementedException(); } private string? GetKubeConfig(string kubeConfigFile) From 48f4614e5d4690819728f9ce8eabac8d569b65f7 Mon Sep 17 00:00:00 2001 From: ThatBen Date: Thu, 16 Jan 2025 10:21:03 +0100 Subject: [PATCH 07/25] working container log connection --- ProjectPlugins/CodexPlugin/CodexDeployment.cs | 16 +--- Tests/CodexContinuousTests/SingleTestRun.cs | 19 +++-- Tests/CodexContinuousTests/StartupChecker.cs | 84 ++++++++++--------- 3 files changed, 56 insertions(+), 63 deletions(-) diff --git a/ProjectPlugins/CodexPlugin/CodexDeployment.cs b/ProjectPlugins/CodexPlugin/CodexDeployment.cs index 8ea5c9ad..0a22deae 100644 --- a/ProjectPlugins/CodexPlugin/CodexDeployment.cs +++ b/ProjectPlugins/CodexPlugin/CodexDeployment.cs @@ -9,7 +9,7 @@ namespace CodexPlugin public CodexDeployment(CodexInstance[] codexInstances, GethDeployment gethDeployment, CodexContractsDeployment codexContractsDeployment, RunningPod? prometheusContainer, RunningPod? discordBotContainer, DeploymentMetadata metadata, - String id) + string id) { Id = id; CodexInstances = codexInstances; @@ -20,7 +20,7 @@ namespace CodexPlugin Metadata = metadata; } - public String Id { get; } + public string Id { get; } public CodexInstance[] CodexInstances { get; } public GethDeployment GethDeployment { get; } public CodexContractsDeployment CodexContractsDeployment { get; } @@ -29,18 +29,6 @@ namespace CodexPlugin public DeploymentMetadata Metadata { get; } } - //public class CodexInstance - //{ - // public CodexInstance(RunningPod pod, DebugInfo info) - // { - // Pod = pod; - // Info = info; - // } - - // public RunningPod Pod { get; } - // public DebugInfo Info { get; } - //} - public class DeploymentMetadata { public DeploymentMetadata(string name, DateTime startUtc, DateTime finishedUtc, string kubeNamespace, diff --git a/Tests/CodexContinuousTests/SingleTestRun.cs b/Tests/CodexContinuousTests/SingleTestRun.cs index 8958c5b6..6617ae5f 100644 --- a/Tests/CodexContinuousTests/SingleTestRun.cs +++ b/Tests/CodexContinuousTests/SingleTestRun.cs @@ -298,17 +298,18 @@ namespace ContinuousTests private RunningPod[] SelectRandomContainers() { - var number = handle.Test.RequiredNumberOfNodes; - var containers = config.CodexDeployment.CodexInstances.Select(i => i.Pod).ToList(); - if (number == -1) return containers.ToArray(); + throw new NotImplementedException(); + //var number = handle.Test.RequiredNumberOfNodes; + //var containers = config.CodexDeployment.CodexInstances.Select(i => i.Pod).ToList(); + //if (number == -1) return containers.ToArray(); - var result = new RunningPod[number]; - for (var i = 0; i < number; i++) - { - result[i] = containers.PickOneRandom(); - } + //var result = new RunningPod[number]; + //for (var i = 0; i < number; i++) + //{ + // result[i] = containers.PickOneRandom(); + //} - return result; + //return result; } } } \ No newline at end of file diff --git a/Tests/CodexContinuousTests/StartupChecker.cs b/Tests/CodexContinuousTests/StartupChecker.cs index 94f2d978..ab3e637d 100644 --- a/Tests/CodexContinuousTests/StartupChecker.cs +++ b/Tests/CodexContinuousTests/StartupChecker.cs @@ -39,25 +39,27 @@ namespace ContinuousTests private void IncludeDeploymentConfiguration(ILog log) { log.Log(""); - var deployment = config.CodexDeployment; - var workflow = entryPoint.Tools.CreateWorkflow(); - foreach (var instance in deployment.CodexInstances) - { - foreach (var container in instance.Pod.Containers) - { - var podInfo = workflow.GetPodInfo(container); - log.Log($"Codex environment variables for '{container.Name}':"); - log.Log( - $"Namespace: {container.RunningPod.StartResult.Cluster.Configuration.KubernetesNamespace} - " + - $"Pod name: {podInfo.Name} - Deployment name: {instance.Pod.StartResult.Deployment.Name}"); - var codexVars = container.Recipe.EnvVars; - foreach (var vars in codexVars) log.Log(vars.ToString()); - log.Log(""); - } - } - log.Log($"Deployment metadata: {JsonConvert.SerializeObject(deployment.Metadata)}"); - log.Log(""); + throw new NotImplementedException(); + //var deployment = config.CodexDeployment; + //var workflow = entryPoint.Tools.CreateWorkflow(); + //foreach (var instance in deployment.CodexInstances) + //{ + // foreach (var container in instance.Pod.Containers) + // { + // var podInfo = workflow.GetPodInfo(container); + // log.Log($"Codex environment variables for '{container.Name}':"); + // log.Log( + // $"Namespace: {container.RunningPod.StartResult.Cluster.Configuration.KubernetesNamespace} - " + + // $"Pod name: {podInfo.Name} - Deployment name: {instance.Pod.StartResult.Deployment.Name}"); + // var codexVars = container.Recipe.EnvVars; + // foreach (var vars in codexVars) log.Log(vars.ToString()); + // log.Log(""); + // } + //} + + //log.Log($"Deployment metadata: {JsonConvert.SerializeObject(deployment.Metadata)}"); + //log.Log(""); } private void PreflightCheck(Configuration config) @@ -91,31 +93,33 @@ namespace ContinuousTests private void CheckCodexNodes(BaseLog log, Configuration config) { - var nodes = entryPoint.CreateInterface() - .WrapCodexContainers(config.CodexDeployment.CodexInstances.Select(i => i.Pod).ToArray()); - var pass = true; - foreach (var n in nodes) - { - cancelToken.ThrowIfCancellationRequested(); + throw new NotImplementedException(); - var address = n.GetApiEndpoint(); - log.Log($"Checking {n.GetName()} @ '{address}'..."); + //var nodes = entryPoint.CreateInterface() + // .WrapCodexContainers(config.CodexDeployment.CodexInstances.Select(i => i.Pod).ToArray()); + //var pass = true; + //foreach (var n in nodes) + //{ + // cancelToken.ThrowIfCancellationRequested(); - if (EnsureOnline(log, n)) - { - log.Log("OK"); - } - else - { - log.Error($"No response from '{address}'."); - pass = false; - } - } + // var address = n.GetApiEndpoint(); + // log.Log($"Checking {n.GetName()} @ '{address}'..."); - if (!pass) - { - throw new Exception("Not all codex nodes responded."); - } + // if (EnsureOnline(log, n)) + // { + // log.Log("OK"); + // } + // else + // { + // log.Error($"No response from '{address}'."); + // pass = false; + // } + //} + + //if (!pass) + //{ + // throw new Exception("Not all codex nodes responded."); + //} } private bool EnsureOnline(BaseLog log, ICodexNode n) From 4a151880d4e636e93cfe643c73ba9a112f9d0d10 Mon Sep 17 00:00:00 2001 From: ThatBen Date: Thu, 16 Jan 2025 11:31:50 +0100 Subject: [PATCH 08/25] Extracts codexClient assembly --- Framework/Core/Core.csproj | 1 + Framework/Core/EntryPoint.cs | 7 +- Framework/Core/PluginTools.cs | 31 ++++--- Framework/Core/ToolsFactory.cs | 11 ++- Framework/KubernetesWorkflow/K8sTimeSet.cs | 42 ++++++++++ Framework/Utils/EthAccount.cs | 20 +++++ .../Utils}/EthAddress.cs | 2 +- .../Utils}/EthTokenExtensions.cs | 2 +- .../Utils}/TestTokenExtensions.cs | 2 +- Framework/{Core => WebUtils}/Endpoint.cs | 11 ++- Framework/{Core => WebUtils}/Http.cs | 8 +- Framework/WebUtils/HttpFactory.cs | 43 ++++++++++ .../TimeSet.cs => WebUtils/WebCallTimeSet.cs} | 39 +-------- Framework/WebUtils/WebUtils.csproj | 18 ++++ .../CodexAccess.cs | 84 ++++--------------- ProjectPlugins/CodexClient/CodexClient.csproj | 39 +++++++++ ProjectPlugins/CodexClient/CodexInstance.cs | 40 +++++++++ .../CodexLogLevel.cs | 2 +- .../CodexLogLine.cs | 2 +- .../{CodexPlugin => CodexClient}/CodexNode.cs | 57 +++++-------- .../CodexTypes.cs | 2 +- .../CodexUtils.cs | 2 +- .../Hooks/CodexHooksFactory.cs | 5 +- .../Hooks/CodexNodeHooks.cs | 5 +- .../{CodexPlugin => CodexClient}/Mapper.cs | 20 ++--- .../MarketplaceAccess.cs | 4 +- .../MarketplaceTypes.cs | 5 +- ProjectPlugins/CodexClient/ProcessControl.cs | 11 +++ .../StoragePurchaseContract.cs | 17 +--- .../TransferSpeeds.cs | 2 +- .../{CodexPlugin => CodexClient}/openapi.yaml | 0 ....cs => CodexInstanceContainerExtension.cs} | 37 -------- ProjectPlugins/CodexPlugin/CodexPlugin.csproj | 11 +-- ProjectPlugins/CodexPlugin/CodexStarter.cs | 1 - .../CodexNodeTranscriptWriter.cs | 1 - ProjectPlugins/CodexPlugin/ProcessControl.cs | 29 ------- ProjectPlugins/CodexPluginPrebuild/Program.cs | 18 ++-- .../{EthAccount.cs => EthAccountGenerator.cs} | 18 +--- .../MarketTests/ContractSuccessfulTest.cs | 3 +- .../MarketTests/MultipleContractsTest.cs | 11 ++- .../BasicTests/ExampleTests.cs | 1 - .../BasicTests/MarketplaceTests.cs | 11 ++- .../UtilityTests/DiscordBotTests.cs | 12 ++- Tools/KeyMaker/Controllers/KeyController.cs | 2 +- cs-codex-dist-testing.sln | 14 ++++ 45 files changed, 383 insertions(+), 320 deletions(-) create mode 100644 Framework/KubernetesWorkflow/K8sTimeSet.cs create mode 100644 Framework/Utils/EthAccount.cs rename {ProjectPlugins/GethPlugin => Framework/Utils}/EthAddress.cs (96%) rename {ProjectPlugins/GethPlugin => Framework/Utils}/EthTokenExtensions.cs (98%) rename {ProjectPlugins/CodexContractsPlugin => Framework/Utils}/TestTokenExtensions.cs (98%) rename Framework/{Core => WebUtils}/Endpoint.cs (97%) rename Framework/{Core => WebUtils}/Http.cs (91%) create mode 100644 Framework/WebUtils/HttpFactory.cs rename Framework/{Core/TimeSet.cs => WebUtils/WebCallTimeSet.cs} (55%) create mode 100644 Framework/WebUtils/WebUtils.csproj rename ProjectPlugins/{CodexPlugin => CodexClient}/CodexAccess.cs (73%) create mode 100644 ProjectPlugins/CodexClient/CodexClient.csproj create mode 100644 ProjectPlugins/CodexClient/CodexInstance.cs rename ProjectPlugins/{CodexPlugin => CodexClient}/CodexLogLevel.cs (82%) rename ProjectPlugins/{CodexPlugin => CodexClient}/CodexLogLine.cs (99%) rename ProjectPlugins/{CodexPlugin => CodexClient}/CodexNode.cs (87%) rename ProjectPlugins/{CodexPlugin => CodexClient}/CodexTypes.cs (99%) rename ProjectPlugins/{CodexPlugin => CodexClient}/CodexUtils.cs (95%) rename ProjectPlugins/{CodexPlugin => CodexClient}/Hooks/CodexHooksFactory.cs (96%) rename ProjectPlugins/{CodexPlugin => CodexClient}/Hooks/CodexNodeHooks.cs (91%) rename ProjectPlugins/{CodexPlugin => CodexClient}/Mapper.cs (94%) rename ProjectPlugins/{CodexPlugin => CodexClient}/MarketplaceAccess.cs (98%) rename ProjectPlugins/{CodexPlugin => CodexClient}/MarketplaceTypes.cs (98%) create mode 100644 ProjectPlugins/CodexClient/ProcessControl.cs rename ProjectPlugins/{CodexPlugin => CodexClient}/StoragePurchaseContract.cs (92%) rename ProjectPlugins/{CodexPlugin => CodexClient}/TransferSpeeds.cs (98%) rename ProjectPlugins/{CodexPlugin => CodexClient}/openapi.yaml (100%) rename ProjectPlugins/CodexPlugin/{CodexInstance.cs => CodexInstanceContainerExtension.cs} (54%) delete mode 100644 ProjectPlugins/CodexPlugin/ProcessControl.cs rename ProjectPlugins/GethPlugin/{EthAccount.cs => EthAccountGenerator.cs} (55%) diff --git a/Framework/Core/Core.csproj b/Framework/Core/Core.csproj index 42f1c7a6..b961cd63 100644 --- a/Framework/Core/Core.csproj +++ b/Framework/Core/Core.csproj @@ -9,6 +9,7 @@ + diff --git a/Framework/Core/EntryPoint.cs b/Framework/Core/EntryPoint.cs index 0db40f3d..558d2870 100644 --- a/Framework/Core/EntryPoint.cs +++ b/Framework/Core/EntryPoint.cs @@ -1,5 +1,6 @@ using KubernetesWorkflow; using Logging; +using WebUtils; namespace Core { @@ -8,16 +9,16 @@ namespace Core private readonly IToolsFactory toolsFactory; private readonly PluginManager manager = new PluginManager(); - public EntryPoint(ILog log, Configuration configuration, string fileManagerRootFolder, ITimeSet timeSet) + public EntryPoint(ILog log, Configuration configuration, string fileManagerRootFolder, IWebCallTimeSet webCallTimeSet, IK8sTimeSet k8STimeSet) { - toolsFactory = new ToolsFactory(log, configuration, fileManagerRootFolder, timeSet); + toolsFactory = new ToolsFactory(log, configuration, fileManagerRootFolder, webCallTimeSet, k8STimeSet); Tools = toolsFactory.CreateTools(); manager.InstantiatePlugins(PluginFinder.GetPluginTypes(), toolsFactory); } public EntryPoint(ILog log, Configuration configuration, string fileManagerRootFolder) - : this(log, configuration, fileManagerRootFolder, new DefaultTimeSet()) + : this(log, configuration, fileManagerRootFolder, new DefaultWebCallTimeSet(), new DefaultK8sTimeSet()) { } diff --git a/Framework/Core/PluginTools.cs b/Framework/Core/PluginTools.cs index 3db27449..e6eb18a6 100644 --- a/Framework/Core/PluginTools.cs +++ b/Framework/Core/PluginTools.cs @@ -1,12 +1,14 @@ using FileUtils; using KubernetesWorkflow; using Logging; +using WebUtils; namespace Core { - public interface IPluginTools : IWorkflowTool, ILogTool, IHttpFactoryTool, IFileTool + public interface IPluginTools : IWorkflowTool, ILogTool, IHttpFactory, IFileTool { - ITimeSet TimeSet { get; } + IWebCallTimeSet WebCallTimeSet { get; } + IK8sTimeSet K8STimeSet { get; } /// /// Deletes kubernetes and tracked file resources. @@ -25,13 +27,6 @@ namespace Core ILog GetLog(); } - public interface IHttpFactoryTool - { - IHttp CreateHttp(string id, Action onClientCreated); - IHttp CreateHttp(string id, Action onClientCreated, ITimeSet timeSet); - IHttp CreateHttp(string id); - } - public interface IFileTool { IFileManager GetFileManager(); @@ -40,18 +35,22 @@ namespace Core internal class PluginTools : IPluginTools { private readonly WorkflowCreator workflowCreator; + private readonly HttpFactory httpFactory; private readonly IFileManager fileManager; private readonly LogPrefixer log; - internal PluginTools(ILog log, WorkflowCreator workflowCreator, string fileManagerRootFolder, ITimeSet timeSet) + internal PluginTools(ILog log, WorkflowCreator workflowCreator, string fileManagerRootFolder, IWebCallTimeSet webCallTimeSet, IK8sTimeSet k8STimeSet) { this.log = new LogPrefixer(log); this.workflowCreator = workflowCreator; - TimeSet = timeSet; + httpFactory = new HttpFactory(log, webCallTimeSet); + WebCallTimeSet = webCallTimeSet; + K8STimeSet = k8STimeSet; fileManager = new FileManager(log, fileManagerRootFolder); } - public ITimeSet TimeSet { get; } + public IWebCallTimeSet WebCallTimeSet { get; } + public IK8sTimeSet K8STimeSet { get; } public void ApplyLogPrefix(string prefix) { @@ -60,17 +59,17 @@ namespace Core public IHttp CreateHttp(string id, Action onClientCreated) { - return CreateHttp(id, onClientCreated, TimeSet); + return httpFactory.CreateHttp(id, onClientCreated); } - public IHttp CreateHttp(string id, Action onClientCreated, ITimeSet ts) + public IHttp CreateHttp(string id, Action onClientCreated, IWebCallTimeSet timeSet) { - return new Http(id, log, ts, onClientCreated); + return httpFactory.CreateHttp(id, onClientCreated, timeSet); } public IHttp CreateHttp(string id) { - return new Http(id, log, TimeSet); + return httpFactory.CreateHttp(id); } public IStartupWorkflow CreateWorkflow(string? namespaceOverride = null) diff --git a/Framework/Core/ToolsFactory.cs b/Framework/Core/ToolsFactory.cs index 96752b6f..0ca30404 100644 --- a/Framework/Core/ToolsFactory.cs +++ b/Framework/Core/ToolsFactory.cs @@ -1,5 +1,6 @@ using KubernetesWorkflow; using Logging; +using WebUtils; namespace Core { @@ -13,19 +14,21 @@ namespace Core private readonly ILog log; private readonly WorkflowCreator workflowCreator; private readonly string fileManagerRootFolder; - private readonly ITimeSet timeSet; + private readonly IWebCallTimeSet webCallTimeSet; + private readonly IK8sTimeSet k8STimeSet; - public ToolsFactory(ILog log, Configuration configuration, string fileManagerRootFolder, ITimeSet timeSet) + public ToolsFactory(ILog log, Configuration configuration, string fileManagerRootFolder, IWebCallTimeSet webCallTimeSet, IK8sTimeSet k8STimeSet) { this.log = log; workflowCreator = new WorkflowCreator(log, configuration); this.fileManagerRootFolder = fileManagerRootFolder; - this.timeSet = timeSet; + this.webCallTimeSet = webCallTimeSet; + this.k8STimeSet = k8STimeSet; } public PluginTools CreateTools() { - return new PluginTools(log, workflowCreator, fileManagerRootFolder, timeSet); + return new PluginTools(log, workflowCreator, fileManagerRootFolder, webCallTimeSet, k8STimeSet); } } } diff --git a/Framework/KubernetesWorkflow/K8sTimeSet.cs b/Framework/KubernetesWorkflow/K8sTimeSet.cs new file mode 100644 index 00000000..5cfc8468 --- /dev/null +++ b/Framework/KubernetesWorkflow/K8sTimeSet.cs @@ -0,0 +1,42 @@ +namespace Core +{ + public interface IK8sTimeSet + { + /// + /// After a failed K8s operation, wait this long before trying again. + /// + TimeSpan K8sOperationRetryDelay(); + + /// + /// Maximum total time to attempt to perform a successful k8s operation. + /// If k8s operations fail during this timespan, retries will be made. + /// + TimeSpan K8sOperationTimeout(); + } + + public class DefaultK8sTimeSet : IK8sTimeSet + { + public TimeSpan K8sOperationRetryDelay() + { + return TimeSpan.FromSeconds(10); + } + + public TimeSpan K8sOperationTimeout() + { + return TimeSpan.FromMinutes(30); + } + } + + public class LongK8sTimeSet : IK8sTimeSet + { + public TimeSpan K8sOperationRetryDelay() + { + return TimeSpan.FromSeconds(30); + } + + public TimeSpan K8sOperationTimeout() + { + return TimeSpan.FromHours(1); + } + } +} diff --git a/Framework/Utils/EthAccount.cs b/Framework/Utils/EthAccount.cs new file mode 100644 index 00000000..0898a30a --- /dev/null +++ b/Framework/Utils/EthAccount.cs @@ -0,0 +1,20 @@ +namespace Utils +{ + [Serializable] + public class EthAccount + { + public EthAccount(EthAddress ethAddress, string privateKey) + { + EthAddress = ethAddress; + PrivateKey = privateKey; + } + + public EthAddress EthAddress { get; } + public string PrivateKey { get; } + + public override string ToString() + { + return EthAddress.ToString(); + } + } +} diff --git a/ProjectPlugins/GethPlugin/EthAddress.cs b/Framework/Utils/EthAddress.cs similarity index 96% rename from ProjectPlugins/GethPlugin/EthAddress.cs rename to Framework/Utils/EthAddress.cs index 9b32cd27..61c2776c 100644 --- a/ProjectPlugins/GethPlugin/EthAddress.cs +++ b/Framework/Utils/EthAddress.cs @@ -1,4 +1,4 @@ -namespace GethPlugin +namespace Utils { public interface IHasEthAddress { diff --git a/ProjectPlugins/GethPlugin/EthTokenExtensions.cs b/Framework/Utils/EthTokenExtensions.cs similarity index 98% rename from ProjectPlugins/GethPlugin/EthTokenExtensions.cs rename to Framework/Utils/EthTokenExtensions.cs index d85533a9..70bd3bb2 100644 --- a/ProjectPlugins/GethPlugin/EthTokenExtensions.cs +++ b/Framework/Utils/EthTokenExtensions.cs @@ -1,4 +1,4 @@ -namespace GethPlugin +namespace Utils { public class Ether : IComparable { diff --git a/ProjectPlugins/CodexContractsPlugin/TestTokenExtensions.cs b/Framework/Utils/TestTokenExtensions.cs similarity index 98% rename from ProjectPlugins/CodexContractsPlugin/TestTokenExtensions.cs rename to Framework/Utils/TestTokenExtensions.cs index ddd66759..11b23917 100644 --- a/ProjectPlugins/CodexContractsPlugin/TestTokenExtensions.cs +++ b/Framework/Utils/TestTokenExtensions.cs @@ -1,6 +1,6 @@ using System.Numerics; -namespace CodexContractsPlugin +namespace Utils { public class TestToken : IComparable { diff --git a/Framework/Core/Endpoint.cs b/Framework/WebUtils/Endpoint.cs similarity index 97% rename from Framework/Core/Endpoint.cs rename to Framework/WebUtils/Endpoint.cs index 28b0c612..bf1a95a1 100644 --- a/Framework/Core/Endpoint.cs +++ b/Framework/WebUtils/Endpoint.cs @@ -1,11 +1,10 @@ -using Logging; -using Newtonsoft.Json; -using Serialization = Newtonsoft.Json.Serialization; -using System.Net.Http.Headers; +using System.Net.Http.Headers; using System.Net.Http.Json; +using Logging; +using Newtonsoft.Json; using Utils; -namespace Core +namespace WebUtils { public interface IEndpoint { @@ -119,7 +118,7 @@ namespace Core var errors = new List(); var deserialized = JsonConvert.DeserializeObject(json, new JsonSerializerSettings() { - Error = delegate (object? sender, Serialization.ErrorEventArgs args) + Error = delegate (object? sender, Newtonsoft.Json.Serialization.ErrorEventArgs args) { if (args.CurrentObject == args.ErrorContext.OriginalObject) { diff --git a/Framework/Core/Http.cs b/Framework/WebUtils/Http.cs similarity index 91% rename from Framework/Core/Http.cs rename to Framework/WebUtils/Http.cs index 601d2f48..98a68da4 100644 --- a/Framework/Core/Http.cs +++ b/Framework/WebUtils/Http.cs @@ -1,7 +1,7 @@ using Logging; using Utils; -namespace Core +namespace WebUtils { public interface IHttp { @@ -16,16 +16,16 @@ namespace Core private static object lockLock = new object(); private static readonly Dictionary httpLocks = new Dictionary(); private readonly ILog log; - private readonly ITimeSet timeSet; + private readonly IWebCallTimeSet timeSet; private readonly Action onClientCreated; private readonly string id; - internal Http(string id, ILog log, ITimeSet timeSet) + internal Http(string id, ILog log, IWebCallTimeSet timeSet) : this(id, log, timeSet, DoNothing) { } - internal Http(string id, ILog log, ITimeSet timeSet, Action onClientCreated) + internal Http(string id, ILog log, IWebCallTimeSet timeSet, Action onClientCreated) { this.id = id; this.log = log; diff --git a/Framework/WebUtils/HttpFactory.cs b/Framework/WebUtils/HttpFactory.cs new file mode 100644 index 00000000..4d5d7c4c --- /dev/null +++ b/Framework/WebUtils/HttpFactory.cs @@ -0,0 +1,43 @@ +using Logging; + +namespace WebUtils +{ + public interface IHttpFactory + { + IHttp CreateHttp(string id, Action onClientCreated); + IHttp CreateHttp(string id, Action onClientCreated, IWebCallTimeSet timeSet); + IHttp CreateHttp(string id); + } + + public class HttpFactory : IHttpFactory + { + private readonly ILog log; + private readonly IWebCallTimeSet defaultTimeSet; + + public HttpFactory(ILog log) + : this (log, new DefaultWebCallTimeSet()) + { + } + + public HttpFactory(ILog log, IWebCallTimeSet defaultTimeSet) + { + this.log = log; + this.defaultTimeSet = defaultTimeSet; + } + + public IHttp CreateHttp(string id, Action onClientCreated) + { + return CreateHttp(id, onClientCreated, defaultTimeSet); + } + + public IHttp CreateHttp(string id, Action onClientCreated, IWebCallTimeSet ts) + { + return new Http(id, log, ts, onClientCreated); + } + + public IHttp CreateHttp(string id) + { + return new Http(id, log, defaultTimeSet); + } + } +} diff --git a/Framework/Core/TimeSet.cs b/Framework/WebUtils/WebCallTimeSet.cs similarity index 55% rename from Framework/Core/TimeSet.cs rename to Framework/WebUtils/WebCallTimeSet.cs index ce47c450..347a77b8 100644 --- a/Framework/Core/TimeSet.cs +++ b/Framework/WebUtils/WebCallTimeSet.cs @@ -1,6 +1,6 @@ -namespace Core +namespace WebUtils { - public interface ITimeSet + public interface IWebCallTimeSet { /// /// Timeout for a single HTTP call. @@ -17,20 +17,9 @@ /// After a failed HTTP call, wait this long before trying again. /// TimeSpan HttpCallRetryDelay(); - - /// - /// After a failed K8s operation, wait this long before trying again. - /// - TimeSpan K8sOperationRetryDelay(); - - /// - /// Maximum total time to attempt to perform a successful k8s operation. - /// If k8s operations fail during this timespan, retries will be made. - /// - TimeSpan K8sOperationTimeout(); } - public class DefaultTimeSet : ITimeSet + public class DefaultWebCallTimeSet : IWebCallTimeSet { public TimeSpan HttpCallTimeout() { @@ -46,19 +35,9 @@ { return TimeSpan.FromSeconds(1); } - - public TimeSpan K8sOperationRetryDelay() - { - return TimeSpan.FromSeconds(10); - } - - public TimeSpan K8sOperationTimeout() - { - return TimeSpan.FromMinutes(30); - } } - public class LongTimeSet : ITimeSet + public class LongWebCallTimeSet : IWebCallTimeSet { public TimeSpan HttpCallTimeout() { @@ -74,15 +53,5 @@ { return TimeSpan.FromSeconds(20); } - - public TimeSpan K8sOperationRetryDelay() - { - return TimeSpan.FromSeconds(30); - } - - public TimeSpan K8sOperationTimeout() - { - return TimeSpan.FromHours(1); - } } } diff --git a/Framework/WebUtils/WebUtils.csproj b/Framework/WebUtils/WebUtils.csproj new file mode 100644 index 00000000..743cc777 --- /dev/null +++ b/Framework/WebUtils/WebUtils.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/ProjectPlugins/CodexPlugin/CodexAccess.cs b/ProjectPlugins/CodexClient/CodexAccess.cs similarity index 73% rename from ProjectPlugins/CodexPlugin/CodexAccess.cs rename to ProjectPlugins/CodexClient/CodexAccess.cs index 227956e5..f700331d 100644 --- a/ProjectPlugins/CodexPlugin/CodexAccess.cs +++ b/ProjectPlugins/CodexClient/CodexAccess.cs @@ -1,26 +1,25 @@ using CodexOpenApi; -using Core; -using GethPlugin; using Logging; using Newtonsoft.Json; using Utils; +using WebUtils; -namespace CodexPlugin +namespace CodexClient { public class CodexAccess { private readonly ILog log; - private readonly IPluginTools tools; + private readonly IHttpFactory httpFactory; private readonly IProcessControl processControl; private ICodexInstance instance; private readonly Mapper mapper = new Mapper(); - public CodexAccess(IPluginTools tools, IProcessControl processControl, ICodexInstance instance, ICrashWatcher crashWatcher) + public CodexAccess(ILog log, IHttpFactory httpFactory, IProcessControl processControl, ICodexInstance instance, ICrashWatcher crashWatcher) { - this.tools = tools; + this.log = log; + this.httpFactory = httpFactory; this.processControl = processControl; this.instance = instance; - log = tools.GetLog(); CrashWatcher = crashWatcher; CrashWatcher.Start(); @@ -103,19 +102,14 @@ namespace CodexPlugin }); } - public string UploadFile(UploadInput uploadInput, Action onFailure) + public string UploadFile(UploadInput uploadInput) { - return OnCodex( - api => api.UploadAsync(uploadInput.ContentType, uploadInput.ContentDisposition, uploadInput.FileStream), - CreateRetryConfig(nameof(UploadFile), onFailure)); + return OnCodex(api => api.UploadAsync(uploadInput.ContentType, uploadInput.ContentDisposition, uploadInput.FileStream)); } - public Stream DownloadFile(string contentId, Action onFailure) + public Stream DownloadFile(string contentId) { - var fileResponse = OnCodex( - api => api.DownloadNetworkStreamAsync(contentId), - CreateRetryConfig(nameof(DownloadFile), onFailure)); - + var fileResponse = OnCodex(api => api.DownloadNetworkStreamAsync(contentId)); if (fileResponse.StatusCode != 200) throw new Exception("Download failed with StatusCode: " + fileResponse.StatusCode); return fileResponse.Stream; } @@ -147,7 +141,6 @@ namespace CodexPlugin return JsonConvert.DeserializeObject(str)!; }, nameof(LocalFiles)); })); - } public StorageAvailability SalesAvailability(StorageAvailability request) @@ -227,22 +220,22 @@ namespace CodexPlugin processControl.DeleteDataDirFolder(instance); } - private T OnCodex(Func> action) + private T OnCodex(Func> action) { - var result = tools.CreateHttp(GetHttpId(), CheckContainerCrashed).OnClient(client => CallCodex(client, action)); + var result = httpFactory.CreateHttp(GetHttpId(), CheckContainerCrashed).OnClient(client => CallCodex(client, action)); return result; } - private T OnCodex(Func> action, Retry retry) + private T OnCodex(Func> action, Retry retry) { - var result = tools.CreateHttp(GetHttpId(), CheckContainerCrashed).OnClient(client => CallCodex(client, action), retry); + var result = httpFactory.CreateHttp(GetHttpId(), CheckContainerCrashed).OnClient(client => CallCodex(client, action), retry); return result; } - private T CallCodex(HttpClient client, Func> action) + private T CallCodex(HttpClient client, Func> action) { var address = GetAddress(); - var api = new CodexApi(client); + var api = new openapiClient(client); api.BaseUrl = $"{address.Host}:{address.Port}/api/codex/v1"; return CrashCheck(() => Time.Wait(action(api))); } @@ -261,7 +254,7 @@ namespace CodexPlugin private IEndpoint GetEndpoint() { - return tools + return httpFactory .CreateHttp(GetHttpId(), CheckContainerCrashed) .CreateEndpoint(GetAddress(), "/api/codex/v1/", GetName()); } @@ -281,49 +274,6 @@ namespace CodexPlugin if (CrashWatcher.HasCrashed()) throw new Exception($"Container {GetName()} has crashed."); } - private Retry CreateRetryConfig(string description, Action onFailure) - { - var timeSet = tools.TimeSet; - - return new Retry(description, timeSet.HttpRetryTimeout(), timeSet.HttpCallRetryDelay(), failure => - { - onFailure(failure); - Investigate(failure, timeSet); - }); - } - - private void Investigate(Failure failure, ITimeSet timeSet) - { - Log($"Retry {failure.TryNumber} took {Time.FormatDuration(failure.Duration)} and failed with '{failure.Exception}'. " + - $"(HTTP timeout = {Time.FormatDuration(timeSet.HttpCallTimeout())}) " + - $"Checking if node responds to debug/info..."); - - try - { - var debugInfo = GetDebugInfo(); - if (string.IsNullOrEmpty(debugInfo.Spr)) - { - Log("Did not get value debug/info response."); - Throw(failure); - } - else - { - Log("Got valid response from debug/info."); - } - } - catch (Exception ex) - { - Log("Got exception from debug/info call: " + ex); - Throw(failure); - } - - if (failure.Duration < timeSet.HttpCallTimeout()) - { - Log("Retry failed within HTTP timeout duration."); - Throw(failure); - } - } - private void Throw(Failure failure) { throw failure.Exception; diff --git a/ProjectPlugins/CodexClient/CodexClient.csproj b/ProjectPlugins/CodexClient/CodexClient.csproj new file mode 100644 index 00000000..dc11b863 --- /dev/null +++ b/ProjectPlugins/CodexClient/CodexClient.csproj @@ -0,0 +1,39 @@ + + + + net8.0 + enable + enable + + + + + + + + + NSwagCSharp + CodexOpenApi + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/ProjectPlugins/CodexClient/CodexInstance.cs b/ProjectPlugins/CodexClient/CodexInstance.cs new file mode 100644 index 00000000..54c2e9c3 --- /dev/null +++ b/ProjectPlugins/CodexClient/CodexInstance.cs @@ -0,0 +1,40 @@ +using Utils; + +namespace CodexClient +{ + public interface ICodexInstance + { + string Name { get; } + string ImageName { get; } + DateTime StartUtc { get; } + Address DiscoveryEndpoint { get; } + Address ApiEndpoint { get; } + Address ListenEndpoint { get; } + EthAccount? EthAccount { get; } + Address? MetricsEndpoint { get; } + } + + public class CodexInstance : ICodexInstance + { + public CodexInstance(string name, string imageName, DateTime startUtc, Address discoveryEndpoint, Address apiEndpoint, Address listenEndpoint, EthAccount? ethAccount, Address? metricsEndpoint) + { + Name = name; + ImageName = imageName; + StartUtc = startUtc; + DiscoveryEndpoint = discoveryEndpoint; + ApiEndpoint = apiEndpoint; + ListenEndpoint = listenEndpoint; + EthAccount = ethAccount; + MetricsEndpoint = metricsEndpoint; + } + + public string Name { get; } + public string ImageName { get; } + public DateTime StartUtc { get; } + public Address DiscoveryEndpoint { get; } + public Address ApiEndpoint { get; } + public Address ListenEndpoint { get; } + public EthAccount? EthAccount { get; } + public Address? MetricsEndpoint { get; } + } +} diff --git a/ProjectPlugins/CodexPlugin/CodexLogLevel.cs b/ProjectPlugins/CodexClient/CodexLogLevel.cs similarity index 82% rename from ProjectPlugins/CodexPlugin/CodexLogLevel.cs rename to ProjectPlugins/CodexClient/CodexLogLevel.cs index a859a0c4..70cd2e3c 100644 --- a/ProjectPlugins/CodexPlugin/CodexLogLevel.cs +++ b/ProjectPlugins/CodexClient/CodexLogLevel.cs @@ -1,4 +1,4 @@ -namespace CodexPlugin +namespace CodexClient { public enum CodexLogLevel { diff --git a/ProjectPlugins/CodexPlugin/CodexLogLine.cs b/ProjectPlugins/CodexClient/CodexLogLine.cs similarity index 99% rename from ProjectPlugins/CodexPlugin/CodexLogLine.cs rename to ProjectPlugins/CodexClient/CodexLogLine.cs index d2758145..01f90775 100644 --- a/ProjectPlugins/CodexPlugin/CodexLogLine.cs +++ b/ProjectPlugins/CodexClient/CodexLogLine.cs @@ -1,6 +1,6 @@ using System.Globalization; -namespace CodexPlugin +namespace CodexClient { public class CodexLogLine { diff --git a/ProjectPlugins/CodexPlugin/CodexNode.cs b/ProjectPlugins/CodexClient/CodexNode.cs similarity index 87% rename from ProjectPlugins/CodexPlugin/CodexNode.cs rename to ProjectPlugins/CodexClient/CodexNode.cs index 12cf0f28..90f09557 100644 --- a/ProjectPlugins/CodexPlugin/CodexNode.cs +++ b/ProjectPlugins/CodexClient/CodexNode.cs @@ -1,14 +1,11 @@ -using CodexPlugin.Hooks; -using Core; +using CodexClient.Hooks; using FileUtils; -using GethPlugin; using Logging; -using MetricsPlugin; using Utils; -namespace CodexPlugin +namespace CodexClient { - public interface ICodexNode : IHasMetricsScrapeTarget, IHasEthAddress + public partial interface ICodexNode : IHasEthAddress { string GetName(); string GetImageName(); @@ -17,10 +14,8 @@ namespace CodexPlugin string GetSpr(); DebugPeer GetDebugPeer(string peerId); ContentId UploadFile(TrackedFile file); - ContentId UploadFile(TrackedFile file, Action onFailure); - ContentId UploadFile(TrackedFile file, string contentType, string contentDisposition, Action onFailure); + ContentId UploadFile(TrackedFile file, string contentType, string contentDisposition); TrackedFile? DownloadContent(ContentId contentId, string fileLabel = ""); - TrackedFile? DownloadContent(ContentId contentId, Action onFailure, string fileLabel = ""); LocalDataset DownloadStreamless(ContentId cid); /// /// TODO: This will monitor the quota-used of the node until 'size' bytes are added. That's a very bad way @@ -54,23 +49,23 @@ namespace CodexPlugin { private const string UploadFailedMessage = "Unable to store block"; private readonly ILog log; - private readonly IPluginTools tools; private readonly ICodexNodeHooks hooks; private readonly TransferSpeeds transferSpeeds; private string peerId = string.Empty; private string nodeId = string.Empty; private readonly CodexAccess codexAccess; + private readonly IFileManager fileManager; - public CodexNode(IPluginTools tools, CodexAccess codexAccess, IMarketplaceAccess marketplaceAccess, ICodexNodeHooks hooks) + public CodexNode(ILog log, CodexAccess codexAccess, IFileManager fileManager, IMarketplaceAccess marketplaceAccess, ICodexNodeHooks hooks) { - this.tools = tools; this.codexAccess = codexAccess; + this.fileManager = fileManager; Marketplace = marketplaceAccess; this.hooks = hooks; Version = new DebugInfoVersion(); transferSpeeds = new TransferSpeeds(); - log = new LogPrefixer(tools.GetLog(), $"{GetName()} "); + this.log = new LogPrefixer(log, $"{GetName()} "); } public void Awake() @@ -156,15 +151,10 @@ namespace CodexPlugin public ContentId UploadFile(TrackedFile file) { - return UploadFile(file, DoNothing); + return UploadFile(file, "application/octet-stream", $"attachment; filename=\"{Path.GetFileName(file.Filename)}\""); } - public ContentId UploadFile(TrackedFile file, Action onFailure) - { - return UploadFile(file, "application/octet-stream", $"attachment; filename=\"{Path.GetFileName(file.Filename)}\"", onFailure); - } - - public ContentId UploadFile(TrackedFile file, string contentType, string contentDisposition, Action onFailure) + public ContentId UploadFile(TrackedFile file, string contentType, string contentDisposition) { using var fileStream = File.OpenRead(file.Filename); var uniqueId = Guid.NewGuid().ToString(); @@ -176,7 +166,7 @@ namespace CodexPlugin var logMessage = $"Uploading file {file.Describe()} with contentType: '{input.ContentType}' and disposition: '{input.ContentDisposition}'..."; var measurement = Stopwatch.Measure(log, logMessage, () => { - return codexAccess.UploadFile(input, onFailure); + return codexAccess.UploadFile(input); }); var response = measurement.Value; @@ -194,17 +184,12 @@ namespace CodexPlugin public TrackedFile? DownloadContent(ContentId contentId, string fileLabel = "") { - return DownloadContent(contentId, DoNothing, fileLabel); - } - - public TrackedFile? DownloadContent(ContentId contentId, Action onFailure, string fileLabel = "") - { - var file = tools.GetFileManager().CreateEmptyFile(fileLabel); + var file = fileManager.CreateEmptyFile(fileLabel); hooks.OnFileDownloading(contentId); Log($"Downloading '{contentId}'..."); var logMessage = $"Downloaded '{contentId}' to '{file.Filename}'"; - var measurement = Stopwatch.Measure(log, logMessage, () => DownloadToFile(contentId.Id, file, onFailure)); + var measurement = Stopwatch.Measure(log, logMessage, () => DownloadToFile(contentId.Id, file)); var size = file.GetFilesize(); transferSpeeds.AddDownloadSample(size, measurement); @@ -337,20 +322,20 @@ namespace CodexPlugin .ToArray(); } - private void DownloadToFile(string contentId, TrackedFile file, Action onFailure) + private void DownloadToFile(string contentId, TrackedFile file) { using var fileStream = File.OpenWrite(file.Filename); - var timeout = tools.TimeSet.HttpCallTimeout(); + var timeout = TimeSpan.FromMinutes(2.0); // todo: make this user-controllable. try { // Type of stream generated by openAPI client does not support timeouts. var start = DateTime.UtcNow; var cts = new CancellationTokenSource(); - var downloadTask = Task.Run((Action)(() => + var downloadTask = Task.Run(() => { - using var downloadStream = this.codexAccess.DownloadFile(contentId, onFailure); - downloadStream.CopyTo((Stream)fileStream); - }), cts.Token); + using var downloadStream = codexAccess.DownloadFile(contentId); + downloadStream.CopyTo(fileStream); + }, cts.Token); while (DateTime.UtcNow - start < timeout) { @@ -411,9 +396,5 @@ namespace CodexPlugin { log.Log(msg); } - - private void DoNothing(Failure failure) - { - } } } diff --git a/ProjectPlugins/CodexPlugin/CodexTypes.cs b/ProjectPlugins/CodexClient/CodexTypes.cs similarity index 99% rename from ProjectPlugins/CodexPlugin/CodexTypes.cs rename to ProjectPlugins/CodexClient/CodexTypes.cs index be5b2295..1d310b34 100644 --- a/ProjectPlugins/CodexPlugin/CodexTypes.cs +++ b/ProjectPlugins/CodexClient/CodexTypes.cs @@ -1,7 +1,7 @@ using Newtonsoft.Json; using Utils; -namespace CodexPlugin +namespace CodexClient { public class DebugInfo { diff --git a/ProjectPlugins/CodexPlugin/CodexUtils.cs b/ProjectPlugins/CodexClient/CodexUtils.cs similarity index 95% rename from ProjectPlugins/CodexPlugin/CodexUtils.cs rename to ProjectPlugins/CodexClient/CodexUtils.cs index e4cf96e6..b791f049 100644 --- a/ProjectPlugins/CodexPlugin/CodexUtils.cs +++ b/ProjectPlugins/CodexClient/CodexUtils.cs @@ -1,4 +1,4 @@ -namespace CodexPlugin +namespace CodexClient { public static class CodexUtils { diff --git a/ProjectPlugins/CodexPlugin/Hooks/CodexHooksFactory.cs b/ProjectPlugins/CodexClient/Hooks/CodexHooksFactory.cs similarity index 96% rename from ProjectPlugins/CodexPlugin/Hooks/CodexHooksFactory.cs rename to ProjectPlugins/CodexClient/Hooks/CodexHooksFactory.cs index b3a718ea..83e45926 100644 --- a/ProjectPlugins/CodexPlugin/Hooks/CodexHooksFactory.cs +++ b/ProjectPlugins/CodexClient/Hooks/CodexHooksFactory.cs @@ -1,7 +1,6 @@ -using GethPlugin; -using Utils; +using Utils; -namespace CodexPlugin.Hooks +namespace CodexClient.Hooks { public interface ICodexHooksProvider { diff --git a/ProjectPlugins/CodexPlugin/Hooks/CodexNodeHooks.cs b/ProjectPlugins/CodexClient/Hooks/CodexNodeHooks.cs similarity index 91% rename from ProjectPlugins/CodexPlugin/Hooks/CodexNodeHooks.cs rename to ProjectPlugins/CodexClient/Hooks/CodexNodeHooks.cs index b2bf6170..8becef0a 100644 --- a/ProjectPlugins/CodexPlugin/Hooks/CodexNodeHooks.cs +++ b/ProjectPlugins/CodexClient/Hooks/CodexNodeHooks.cs @@ -1,7 +1,6 @@ -using GethPlugin; -using Utils; +using Utils; -namespace CodexPlugin.Hooks +namespace CodexClient.Hooks { public interface ICodexNodeHooks { diff --git a/ProjectPlugins/CodexPlugin/Mapper.cs b/ProjectPlugins/CodexClient/Mapper.cs similarity index 94% rename from ProjectPlugins/CodexPlugin/Mapper.cs rename to ProjectPlugins/CodexClient/Mapper.cs index 73319fe3..5d14fbe6 100644 --- a/ProjectPlugins/CodexPlugin/Mapper.cs +++ b/ProjectPlugins/CodexClient/Mapper.cs @@ -1,10 +1,8 @@ -using CodexContractsPlugin; -using CodexOpenApi; -using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Linq; using System.Numerics; using Utils; -namespace CodexPlugin +namespace CodexClient { public class Mapper { @@ -80,12 +78,12 @@ namespace CodexPlugin }; } - public StorageAvailability[] Map(ICollection availabilities) + public StorageAvailability[] Map(ICollection availabilities) { return availabilities.Select(a => Map(a)).ToArray(); } - public StorageAvailability Map(SalesAvailabilityREAD availability) + public StorageAvailability Map(CodexOpenApi.SalesAvailabilityREAD availability) { return new StorageAvailability ( @@ -142,7 +140,7 @@ namespace CodexPlugin // }; //} - public CodexSpace Map(Space space) + public CodexSpace Map(CodexOpenApi.Space space) { return new CodexSpace { @@ -153,7 +151,7 @@ namespace CodexPlugin }; } - private DebugInfoVersion Map(CodexVersion obj) + private DebugInfoVersion Map(CodexOpenApi.CodexVersion obj) { return new DebugInfoVersion { @@ -162,7 +160,7 @@ namespace CodexPlugin }; } - private DebugInfoTable Map(PeersTable obj) + private DebugInfoTable Map(CodexOpenApi.PeersTable obj) { return new DebugInfoTable { @@ -171,7 +169,7 @@ namespace CodexPlugin }; } - private DebugInfoTableNode Map(Node? token) + private DebugInfoTableNode Map(CodexOpenApi.Node? token) { if (token == null) return new DebugInfoTableNode(); return new DebugInfoTableNode @@ -184,7 +182,7 @@ namespace CodexPlugin }; } - private DebugInfoTableNode[] Map(ICollection nodes) + private DebugInfoTableNode[] Map(ICollection nodes) { if (nodes == null || nodes.Count == 0) { diff --git a/ProjectPlugins/CodexPlugin/MarketplaceAccess.cs b/ProjectPlugins/CodexClient/MarketplaceAccess.cs similarity index 98% rename from ProjectPlugins/CodexPlugin/MarketplaceAccess.cs rename to ProjectPlugins/CodexClient/MarketplaceAccess.cs index 34720df7..c7a14142 100644 --- a/ProjectPlugins/CodexPlugin/MarketplaceAccess.cs +++ b/ProjectPlugins/CodexClient/MarketplaceAccess.cs @@ -1,8 +1,8 @@ -using CodexPlugin.Hooks; +using CodexClient.Hooks; using Logging; using Utils; -namespace CodexPlugin +namespace CodexClient { public interface IMarketplaceAccess { diff --git a/ProjectPlugins/CodexPlugin/MarketplaceTypes.cs b/ProjectPlugins/CodexClient/MarketplaceTypes.cs similarity index 98% rename from ProjectPlugins/CodexPlugin/MarketplaceTypes.cs rename to ProjectPlugins/CodexClient/MarketplaceTypes.cs index 59626298..d0319bee 100644 --- a/ProjectPlugins/CodexPlugin/MarketplaceTypes.cs +++ b/ProjectPlugins/CodexClient/MarketplaceTypes.cs @@ -1,8 +1,7 @@ -using CodexContractsPlugin; -using Logging; +using Logging; using Utils; -namespace CodexPlugin +namespace CodexClient { public class StoragePurchaseRequest { diff --git a/ProjectPlugins/CodexClient/ProcessControl.cs b/ProjectPlugins/CodexClient/ProcessControl.cs new file mode 100644 index 00000000..61dcb8f2 --- /dev/null +++ b/ProjectPlugins/CodexClient/ProcessControl.cs @@ -0,0 +1,11 @@ +using Logging; + +namespace CodexClient +{ + public interface IProcessControl + { + void Stop(ICodexInstance instance, bool waitTillStopped); + IDownloadedLog DownloadLog(ICodexInstance instance, LogFile file); + void DeleteDataDirFolder(ICodexInstance instance); + } +} diff --git a/ProjectPlugins/CodexPlugin/StoragePurchaseContract.cs b/ProjectPlugins/CodexClient/StoragePurchaseContract.cs similarity index 92% rename from ProjectPlugins/CodexPlugin/StoragePurchaseContract.cs rename to ProjectPlugins/CodexClient/StoragePurchaseContract.cs index 487cea43..7e4f0d00 100644 --- a/ProjectPlugins/CodexPlugin/StoragePurchaseContract.cs +++ b/ProjectPlugins/CodexClient/StoragePurchaseContract.cs @@ -1,11 +1,9 @@ -using CodexContractsPlugin; -using CodexPlugin.Hooks; -using GethPlugin; +using CodexClient.Hooks; using Logging; using Newtonsoft.Json; using Utils; -namespace CodexPlugin +namespace CodexClient { public interface IStoragePurchaseContract { @@ -14,7 +12,7 @@ namespace CodexPlugin ContentId ContentId { get; } void WaitForStorageContractSubmitted(); void WaitForStorageContractStarted(); - void WaitForStorageContractFinished(ICodexContracts contracts); + void WaitForStorageContractFinished(); void WaitForContractFailed(); } @@ -65,7 +63,7 @@ namespace CodexPlugin AssertDuration(SubmittedToStarted, timeout, nameof(SubmittedToStarted)); } - public void WaitForStorageContractFinished(ICodexContracts contracts) + public void WaitForStorageContractFinished() { if (!contractStartedUtc.HasValue) { @@ -77,13 +75,6 @@ namespace CodexPlugin contractFinishedUtc = DateTime.UtcNow; LogFinishedDuration(); AssertDuration(SubmittedToFinished, timeout, nameof(SubmittedToFinished)); - - contracts.WaitUntilNextPeriod(); - contracts.WaitUntilNextPeriod(); - - var blocks = 3; - Log($"Waiting {blocks} blocks for nodes to process payouts..."); - Thread.Sleep(GethContainerRecipe.BlockInterval * blocks); } public void WaitForContractFailed() diff --git a/ProjectPlugins/CodexPlugin/TransferSpeeds.cs b/ProjectPlugins/CodexClient/TransferSpeeds.cs similarity index 98% rename from ProjectPlugins/CodexPlugin/TransferSpeeds.cs rename to ProjectPlugins/CodexClient/TransferSpeeds.cs index b745f644..9e013efb 100644 --- a/ProjectPlugins/CodexPlugin/TransferSpeeds.cs +++ b/ProjectPlugins/CodexClient/TransferSpeeds.cs @@ -1,6 +1,6 @@ using Utils; -namespace CodexPlugin +namespace CodexClient { public interface ITransferSpeeds { diff --git a/ProjectPlugins/CodexPlugin/openapi.yaml b/ProjectPlugins/CodexClient/openapi.yaml similarity index 100% rename from ProjectPlugins/CodexPlugin/openapi.yaml rename to ProjectPlugins/CodexClient/openapi.yaml diff --git a/ProjectPlugins/CodexPlugin/CodexInstance.cs b/ProjectPlugins/CodexPlugin/CodexInstanceContainerExtension.cs similarity index 54% rename from ProjectPlugins/CodexPlugin/CodexInstance.cs rename to ProjectPlugins/CodexPlugin/CodexInstanceContainerExtension.cs index 15cde305..5e97dd0f 100644 --- a/ProjectPlugins/CodexPlugin/CodexInstance.cs +++ b/ProjectPlugins/CodexPlugin/CodexInstanceContainerExtension.cs @@ -1,47 +1,10 @@ using Core; -using GethPlugin; using KubernetesWorkflow.Types; using Logging; using Utils; namespace CodexPlugin { - public interface ICodexInstance - { - string Name { get; } - string ImageName { get; } - DateTime StartUtc { get; } - Address DiscoveryEndpoint { get; } - Address ApiEndpoint { get; } - Address ListenEndpoint { get; } - EthAccount? EthAccount { get; } - Address? MetricsEndpoint { get; } - } - - public class CodexInstance : ICodexInstance - { - public CodexInstance(string name, string imageName, DateTime startUtc, Address discoveryEndpoint, Address apiEndpoint, Address listenEndpoint, EthAccount? ethAccount, Address? metricsEndpoint) - { - Name = name; - ImageName = imageName; - StartUtc = startUtc; - DiscoveryEndpoint = discoveryEndpoint; - ApiEndpoint = apiEndpoint; - ListenEndpoint = listenEndpoint; - EthAccount = ethAccount; - MetricsEndpoint = metricsEndpoint; - } - - public string Name { get; } - public string ImageName { get; } - public DateTime StartUtc { get; } - public Address DiscoveryEndpoint { get; } - public Address ApiEndpoint { get; } - public Address ListenEndpoint { get; } - public EthAccount? EthAccount { get; } - public Address? MetricsEndpoint { get; } - } - public static class CodexInstanceContainerExtension { public static ICodexInstance CreateFromPod(RunningPod pod) diff --git a/ProjectPlugins/CodexPlugin/CodexPlugin.csproj b/ProjectPlugins/CodexPlugin/CodexPlugin.csproj index 4e3c96ff..6077bde4 100644 --- a/ProjectPlugins/CodexPlugin/CodexPlugin.csproj +++ b/ProjectPlugins/CodexPlugin/CodexPlugin.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -6,14 +6,6 @@ enable - - - - - - - - all @@ -30,6 +22,7 @@ + diff --git a/ProjectPlugins/CodexPlugin/CodexStarter.cs b/ProjectPlugins/CodexPlugin/CodexStarter.cs index 9dd44aa6..a2baf96f 100644 --- a/ProjectPlugins/CodexPlugin/CodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/CodexStarter.cs @@ -1,6 +1,5 @@ using CodexPlugin.Hooks; using Core; -using GethPlugin; using KubernetesWorkflow; using KubernetesWorkflow.Types; using Logging; diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexNodeTranscriptWriter.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexNodeTranscriptWriter.cs index 6a6cdfca..c221806e 100644 --- a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexNodeTranscriptWriter.cs +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexNodeTranscriptWriter.cs @@ -1,5 +1,4 @@ using CodexPlugin.Hooks; -using GethPlugin; using OverwatchTranscript; using Utils; diff --git a/ProjectPlugins/CodexPlugin/ProcessControl.cs b/ProjectPlugins/CodexPlugin/ProcessControl.cs deleted file mode 100644 index 8b138e49..00000000 --- a/ProjectPlugins/CodexPlugin/ProcessControl.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Logging; - -namespace CodexPlugin -{ - public interface IProcessControl - { - void Stop(ICodexInstance instance, bool waitTillStopped); - IDownloadedLog DownloadLog(ICodexInstance instance, LogFile file); - void DeleteDataDirFolder(ICodexInstance instance); - } - - - //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); - // } - //} - -} diff --git a/ProjectPlugins/CodexPluginPrebuild/Program.cs b/ProjectPlugins/CodexPluginPrebuild/Program.cs index 3df9f235..fcdf8b5b 100644 --- a/ProjectPlugins/CodexPluginPrebuild/Program.cs +++ b/ProjectPlugins/CodexPluginPrebuild/Program.cs @@ -12,11 +12,12 @@ public static class Program { Console.WriteLine("Injecting hash of 'openapi.yaml'..."); - var root = FindCodexPluginFolder(); - Console.WriteLine("Located CodexPlugin: " + root); - var openApiFile = Path.Combine(root, "openapi.yaml"); - var clientFile = Path.Combine(root, "obj", "openapiClient.cs"); - var targetFile = Path.Combine(root, "ApiChecker.cs"); + var pluginRoot = FindCodexPluginFolder(); + var clientRoot = FindCodexClientFolder(); + Console.WriteLine("Located CodexPlugin: " + pluginRoot); + var openApiFile = Path.Combine(pluginRoot, "openapi.yaml"); + var clientFile = Path.Combine(clientRoot, "obj", "openapiClient.cs"); + var targetFile = Path.Combine(pluginRoot, "ApiChecker.cs"); // Force client rebuild by deleting previous artifact. File.Delete(clientFile); @@ -46,6 +47,13 @@ public static class Program return folder; } + private static string FindCodexClientFolder() + { + var folder = Path.Combine(PluginPathUtils.ProjectPluginsDir, "CodexClient"); + if (!Directory.Exists(folder)) throw new Exception("CodexClient folder not found. Expected: " + folder); + return folder; + } + private static string CreateHash(string openApiFile) { var file = File.ReadAllText(openApiFile); diff --git a/ProjectPlugins/GethPlugin/EthAccount.cs b/ProjectPlugins/GethPlugin/EthAccountGenerator.cs similarity index 55% rename from ProjectPlugins/GethPlugin/EthAccount.cs rename to ProjectPlugins/GethPlugin/EthAccountGenerator.cs index 60bf40d8..ed570947 100644 --- a/ProjectPlugins/GethPlugin/EthAccount.cs +++ b/ProjectPlugins/GethPlugin/EthAccountGenerator.cs @@ -1,20 +1,11 @@ using Nethereum.Hex.HexConvertors.Extensions; using Nethereum.Web3.Accounts; +using Utils; namespace GethPlugin { - [Serializable] - public class EthAccount + public static class EthAccountGenerator { - public EthAccount(EthAddress ethAddress, string privateKey) - { - EthAddress = ethAddress; - PrivateKey = privateKey; - } - - public EthAddress EthAddress { get; } - public string PrivateKey { get; } - public static EthAccount GenerateNew() { var ecKey = Nethereum.Signer.EthECKey.GenerateKey(); @@ -24,10 +15,5 @@ namespace GethPlugin return new EthAccount(ethAddress, account.PrivateKey); } - - public override string ToString() - { - return EthAddress.ToString(); - } } } diff --git a/Tests/CodexReleaseTests/MarketTests/ContractSuccessfulTest.cs b/Tests/CodexReleaseTests/MarketTests/ContractSuccessfulTest.cs index b007945d..d614840f 100644 --- a/Tests/CodexReleaseTests/MarketTests/ContractSuccessfulTest.cs +++ b/Tests/CodexReleaseTests/MarketTests/ContractSuccessfulTest.cs @@ -1,4 +1,5 @@ -using CodexContractsPlugin; +using CodexClient; +using CodexContractsPlugin; using CodexPlugin; using GethPlugin; using NUnit.Framework; diff --git a/Tests/CodexReleaseTests/MarketTests/MultipleContractsTest.cs b/Tests/CodexReleaseTests/MarketTests/MultipleContractsTest.cs index 50293c1d..97876f2b 100644 --- a/Tests/CodexReleaseTests/MarketTests/MultipleContractsTest.cs +++ b/Tests/CodexReleaseTests/MarketTests/MultipleContractsTest.cs @@ -1,4 +1,5 @@ -using CodexContractsPlugin; +using CodexClient; +using CodexContractsPlugin; using CodexPlugin; using GethPlugin; using NUnit.Framework; @@ -37,6 +38,14 @@ namespace CodexReleaseTests.MarketTests All(requests, r => r.WaitForStorageContractFinished(GetContracts())); + // todo: removed from codexclient: + //contracts.WaitUntilNextPeriod(); + //contracts.WaitUntilNextPeriod(); + + //var blocks = 3; + //Log($"Waiting {blocks} blocks for nodes to process payouts..."); + //Thread.Sleep(GethContainerRecipe.BlockInterval * blocks); + // todo: //AssertClientHasPaidForContract(pricePerSlotPerSecond, client, request, hosts); //AssertHostsWerePaidForContract(pricePerSlotPerSecond, request, hosts); diff --git a/Tests/ExperimentalTests/BasicTests/ExampleTests.cs b/Tests/ExperimentalTests/BasicTests/ExampleTests.cs index 50367969..fc83da3b 100644 --- a/Tests/ExperimentalTests/BasicTests/ExampleTests.cs +++ b/Tests/ExperimentalTests/BasicTests/ExampleTests.cs @@ -1,6 +1,5 @@ using CodexPlugin; using DistTestCore; -using GethPlugin; using MetricsPlugin; using NUnit.Framework; using Utils; diff --git a/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs b/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs index 16f6dd63..8ce80571 100644 --- a/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs +++ b/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs @@ -1,4 +1,5 @@ -using CodexContractsPlugin; +using CodexClient; +using CodexContractsPlugin; using CodexContractsPlugin.Marketplace; using CodexPlugin; using FileUtils; @@ -109,6 +110,14 @@ namespace CodexTests.BasicTests purchaseContract.WaitForStorageContractFinished(contracts); + // todo: removed from codexclient: + //contracts.WaitUntilNextPeriod(); + //contracts.WaitUntilNextPeriod(); + + //var blocks = 3; + //Log($"Waiting {blocks} blocks for nodes to process payouts..."); + //Thread.Sleep(GethContainerRecipe.BlockInterval * blocks); + AssertBalance(contracts, client, Is.LessThan(clientInitialBalance), "Buyer was not charged for storage."); Assert.That(contracts.GetRequestState(request), Is.EqualTo(RequestState.Finished)); } diff --git a/Tests/ExperimentalTests/UtilityTests/DiscordBotTests.cs b/Tests/ExperimentalTests/UtilityTests/DiscordBotTests.cs index d157f66e..5559c5f6 100644 --- a/Tests/ExperimentalTests/UtilityTests/DiscordBotTests.cs +++ b/Tests/ExperimentalTests/UtilityTests/DiscordBotTests.cs @@ -1,4 +1,5 @@ -using CodexContractsPlugin; +using CodexClient; +using CodexContractsPlugin; using CodexDiscordBotPlugin; using CodexPlugin; using Core; @@ -46,6 +47,15 @@ namespace CodexTests.UtilityTests var purchaseContract = ClientPurchasesStorage(client); purchaseContract.WaitForStorageContractStarted(); purchaseContract.WaitForStorageContractFinished(contracts); + + // todo: removed from codexclient: + //contracts.WaitUntilNextPeriod(); + //contracts.WaitUntilNextPeriod(); + + //var blocks = 3; + //Log($"Waiting {blocks} blocks for nodes to process payouts..."); + //Thread.Sleep(GethContainerRecipe.BlockInterval * blocks); + Thread.Sleep(rewarderInterval * 3); apiCalls.Stop(); diff --git a/Tools/KeyMaker/Controllers/KeyController.cs b/Tools/KeyMaker/Controllers/KeyController.cs index dd218893..09644e9c 100644 --- a/Tools/KeyMaker/Controllers/KeyController.cs +++ b/Tools/KeyMaker/Controllers/KeyController.cs @@ -1,5 +1,5 @@ -using GethPlugin; using Microsoft.AspNetCore.Mvc; +using Utils; namespace KeyMaker.Controllers { diff --git a/cs-codex-dist-testing.sln b/cs-codex-dist-testing.sln index 7ba4155b..9b1c0977 100644 --- a/cs-codex-dist-testing.sln +++ b/cs-codex-dist-testing.sln @@ -82,6 +82,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExperimentalTests", "Tests\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlockchainUtils", "Framework\BlockchainUtils\BlockchainUtils.csproj", "{4648B5AA-A0A7-44BA-89BC-2FD57370943C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodexClient", "ProjectPlugins\CodexClient\CodexClient.csproj", "{9AF12703-29AF-416D-9781-204223D6D0E5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebUtils", "Framework\WebUtils\WebUtils.csproj", "{372C9E5D-5453-4D45-9948-E9324E21AD65}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -220,6 +224,14 @@ Global {4648B5AA-A0A7-44BA-89BC-2FD57370943C}.Debug|Any CPU.Build.0 = Debug|Any CPU {4648B5AA-A0A7-44BA-89BC-2FD57370943C}.Release|Any CPU.ActiveCfg = Release|Any CPU {4648B5AA-A0A7-44BA-89BC-2FD57370943C}.Release|Any CPU.Build.0 = Release|Any CPU + {9AF12703-29AF-416D-9781-204223D6D0E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9AF12703-29AF-416D-9781-204223D6D0E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9AF12703-29AF-416D-9781-204223D6D0E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9AF12703-29AF-416D-9781-204223D6D0E5}.Release|Any CPU.Build.0 = Release|Any CPU + {372C9E5D-5453-4D45-9948-E9324E21AD65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {372C9E5D-5453-4D45-9948-E9324E21AD65}.Debug|Any CPU.Build.0 = Debug|Any CPU + {372C9E5D-5453-4D45-9948-E9324E21AD65}.Release|Any CPU.ActiveCfg = Release|Any CPU + {372C9E5D-5453-4D45-9948-E9324E21AD65}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -258,6 +270,8 @@ Global {639A0603-4E80-465B-BB59-AB02F1DEEF5A} = {88C2A621-8A98-4D07-8625-7900FC8EF89E} {BA7369CD-7C2F-4075-8E35-98BCC19EE203} = {88C2A621-8A98-4D07-8625-7900FC8EF89E} {4648B5AA-A0A7-44BA-89BC-2FD57370943C} = {81AE04BC-CBFA-4E6F-B039-8208E9AFAAE7} + {9AF12703-29AF-416D-9781-204223D6D0E5} = {8F1F1C2A-E313-4E0C-BE40-58FB0BA91124} + {372C9E5D-5453-4D45-9948-E9324E21AD65} = {81AE04BC-CBFA-4E6F-B039-8208E9AFAAE7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {237BF0AA-9EC4-4659-AD9A-65DEB974250C} From c73fa186fc321928a571545d80725652de06ae84 Mon Sep 17 00:00:00 2001 From: ThatBen Date: Thu, 16 Jan 2025 13:24:57 +0100 Subject: [PATCH 09/25] wip --- .../ContainerCrashWatcher.cs | 3 +- Framework/Utils/CrashWatcher.cs | 12 +++--- ProjectPlugins/CodexClient/CodexAccess.cs | 31 +++++++------- ProjectPlugins/CodexClient/CodexNode.cs | 20 +++++----- .../CodexClient/CodexNodeFactory.cs | 40 +++++++++++++++++++ ProjectPlugins/CodexClient/ProcessControl.cs | 12 ++++-- .../ChainStateChangeHandlerMux.cs | 4 +- .../ChainMonitor/ChainStateRequest.cs | 2 +- .../DoNothingChainEventHandler.cs | 4 +- .../ContractInteractions.cs | 2 +- .../Marketplace/Customizations.cs | 2 +- .../CodexPlugin/CodexNodeFactory.cs | 35 ---------------- ProjectPlugins/CodexPlugin/CodexNodeGroup.cs | 5 ++- ProjectPlugins/CodexPlugin/CodexPlugin.cs | 3 +- ProjectPlugins/CodexPlugin/CodexSetup.cs | 5 ++- ProjectPlugins/MetricsPlugin/MetricsQuery.cs | 1 + 16 files changed, 96 insertions(+), 85 deletions(-) create mode 100644 ProjectPlugins/CodexClient/CodexNodeFactory.cs delete mode 100644 ProjectPlugins/CodexPlugin/CodexNodeFactory.cs diff --git a/Framework/KubernetesWorkflow/ContainerCrashWatcher.cs b/Framework/KubernetesWorkflow/ContainerCrashWatcher.cs index 1cb9cde8..2932b01f 100644 --- a/Framework/KubernetesWorkflow/ContainerCrashWatcher.cs +++ b/Framework/KubernetesWorkflow/ContainerCrashWatcher.cs @@ -1,10 +1,9 @@ using k8s; using Logging; -using Utils; namespace KubernetesWorkflow { - public class ContainerCrashWatcher : ICrashWatcher + public class ContainerCrashWatcher { private readonly ILog log; private readonly KubernetesClientConfiguration config; diff --git a/Framework/Utils/CrashWatcher.cs b/Framework/Utils/CrashWatcher.cs index 2be46ab9..9477bdf8 100644 --- a/Framework/Utils/CrashWatcher.cs +++ b/Framework/Utils/CrashWatcher.cs @@ -1,9 +1,9 @@ namespace Utils { - public interface ICrashWatcher - { - void Start(); - void Stop(); - bool HasCrashed(); - } + //public interface ICrashWatcher + //{ + // void Start(); + // void Stop(); + // bool HasCrashed(); + //} } diff --git a/ProjectPlugins/CodexClient/CodexAccess.cs b/ProjectPlugins/CodexClient/CodexAccess.cs index f700331d..434d7946 100644 --- a/ProjectPlugins/CodexClient/CodexAccess.cs +++ b/ProjectPlugins/CodexClient/CodexAccess.cs @@ -14,30 +14,24 @@ namespace CodexClient private ICodexInstance instance; private readonly Mapper mapper = new Mapper(); - public CodexAccess(ILog log, IHttpFactory httpFactory, IProcessControl processControl, ICodexInstance instance, ICrashWatcher crashWatcher) + public CodexAccess(ILog log, IHttpFactory httpFactory, IProcessControl processControl, ICodexInstance instance) { this.log = log; this.httpFactory = httpFactory; this.processControl = processControl; this.instance = instance; - CrashWatcher = crashWatcher; - - CrashWatcher.Start(); } - public ICrashWatcher CrashWatcher { get; } - public void Stop(bool waitTillStopped) { - CrashWatcher.Stop(); - processControl.Stop(instance, waitTillStopped); + processControl.Stop(waitTillStopped); // Prevents accidental use after stop: instance = null!; } public IDownloadedLog DownloadLog(string additionalName = "") { - return processControl.DownloadLog(instance, log.CreateSubfile(GetName() + additionalName)); + return processControl.DownloadLog(log.CreateSubfile(GetName() + additionalName)); } public string GetImageName() @@ -205,6 +199,11 @@ namespace CodexClient return instance.ListenEndpoint; } + public bool HasCrashed() + { + return processControl.HasCrashed(); + } + public Address? GetMetricsEndpoint() { return instance.MetricsEndpoint; @@ -217,18 +216,18 @@ namespace CodexClient public void DeleteDataDirFolder() { - processControl.DeleteDataDirFolder(instance); + processControl.DeleteDataDirFolder(); } private T OnCodex(Func> action) { - var result = httpFactory.CreateHttp(GetHttpId(), CheckContainerCrashed).OnClient(client => CallCodex(client, action)); + var result = httpFactory.CreateHttp(GetHttpId(), h => CheckContainerCrashed()).OnClient(client => CallCodex(client, action)); return result; } private T OnCodex(Func> action, Retry retry) { - var result = httpFactory.CreateHttp(GetHttpId(), CheckContainerCrashed).OnClient(client => CallCodex(client, action), retry); + var result = httpFactory.CreateHttp(GetHttpId(), h => CheckContainerCrashed()).OnClient(client => CallCodex(client, action), retry); return result; } @@ -248,14 +247,14 @@ namespace CodexClient } finally { - CrashWatcher.HasCrashed(); + CheckContainerCrashed(); } } private IEndpoint GetEndpoint() { return httpFactory - .CreateHttp(GetHttpId(), CheckContainerCrashed) + .CreateHttp(GetHttpId(), h => CheckContainerCrashed()) .CreateEndpoint(GetAddress(), "/api/codex/v1/", GetName()); } @@ -269,9 +268,9 @@ namespace CodexClient return GetAddress().ToString(); } - private void CheckContainerCrashed(HttpClient client) + private void CheckContainerCrashed() { - if (CrashWatcher.HasCrashed()) throw new Exception($"Container {GetName()} has crashed."); + if (processControl.HasCrashed()) throw new Exception($"Container {GetName()} has crashed."); } private void Throw(Failure failure) diff --git a/ProjectPlugins/CodexClient/CodexNode.cs b/ProjectPlugins/CodexClient/CodexNode.cs index 90f09557..45cfcd7b 100644 --- a/ProjectPlugins/CodexClient/CodexNode.cs +++ b/ProjectPlugins/CodexClient/CodexNode.cs @@ -34,6 +34,7 @@ namespace CodexClient Address GetDiscoveryEndpoint(); Address GetApiEndpoint(); Address GetListenEndpoint(); + Address GetMetricsScrapeTarget(); /// /// Warning! The node is not usable after this. @@ -85,16 +86,6 @@ namespace CodexClient public DebugInfoVersion Version { get; private set; } public ITransferSpeeds TransferSpeeds { get => transferSpeeds; } - public Address MetricsScrapeTarget - { - get - { - var address = codexAccess.GetMetricsEndpoint(); - if (address == null) throw new Exception("Metrics ScrapeTarget accessed, but node was not started with EnableMetrics()"); - return address; - } - } - public EthAddress EthAddress { get @@ -278,9 +269,16 @@ namespace CodexClient return codexAccess.GetListenEndpoint(); } + public Address GetMetricsScrapeTarget() + { + var address = codexAccess.GetMetricsEndpoint(); + if (address == null) throw new Exception("Metrics ScrapeTarget accessed, but node was not started with EnableMetrics()"); + return address; + } + public bool HasCrashed() { - return codexAccess.CrashWatcher.HasCrashed(); + return codexAccess.HasCrashed(); } public override string ToString() diff --git a/ProjectPlugins/CodexClient/CodexNodeFactory.cs b/ProjectPlugins/CodexClient/CodexNodeFactory.cs new file mode 100644 index 00000000..447ae018 --- /dev/null +++ b/ProjectPlugins/CodexClient/CodexNodeFactory.cs @@ -0,0 +1,40 @@ +using CodexClient.Hooks; +using FileUtils; +using Logging; +using WebUtils; + +namespace CodexClient +{ + public class CodexNodeFactory + { + private readonly ILog log; + private readonly IFileManager fileManager; + private readonly ICodexHooksProvider hooksProvider; + private readonly IHttpFactory httpFactory; + private readonly IIProcessControlFactory processControlFactory; + + public CodexNodeFactory(ILog log, IFileManager fileManager, ICodexHooksProvider hooksProvider, IHttpFactory httpFactory, IIProcessControlFactory processControlFactory) + { + this.log = log; + this.fileManager = fileManager; + this.hooksProvider = hooksProvider; + this.httpFactory = httpFactory; + this.processControlFactory = processControlFactory; + } + + public ICodexNode CreateCodexNode(ICodexInstance instance) + { + var processControl = processControlFactory.CreateProcessControl(instance); + var access = new CodexAccess(log, httpFactory, processControl, instance); + var hooks = hooksProvider.CreateHooks(access.GetName()); + var marketplaceAccess = CreateMarketplaceAccess(instance, access, hooks); + return new CodexNode(log, access, fileManager, marketplaceAccess, hooks); + } + + private IMarketplaceAccess CreateMarketplaceAccess(ICodexInstance instance, CodexAccess access, ICodexNodeHooks hooks) + { + if (instance.EthAccount == null) return new MarketplaceUnavailable(); + return new MarketplaceAccess(log, access, hooks); + } + } +} diff --git a/ProjectPlugins/CodexClient/ProcessControl.cs b/ProjectPlugins/CodexClient/ProcessControl.cs index 61dcb8f2..f431f58b 100644 --- a/ProjectPlugins/CodexClient/ProcessControl.cs +++ b/ProjectPlugins/CodexClient/ProcessControl.cs @@ -2,10 +2,16 @@ namespace CodexClient { + public interface IIProcessControlFactory + { + IProcessControl CreateProcessControl(ICodexInstance instance); + } + public interface IProcessControl { - void Stop(ICodexInstance instance, bool waitTillStopped); - IDownloadedLog DownloadLog(ICodexInstance instance, LogFile file); - void DeleteDataDirFolder(ICodexInstance instance); + void Stop(bool waitTillStopped); + IDownloadedLog DownloadLog(LogFile file); + void DeleteDataDirFolder(); + bool HasCrashed(); } } diff --git a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateChangeHandlerMux.cs b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateChangeHandlerMux.cs index 799573f6..215eeeff 100644 --- a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateChangeHandlerMux.cs +++ b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateChangeHandlerMux.cs @@ -1,5 +1,5 @@ -using GethPlugin; -using System.Numerics; +using System.Numerics; +using Utils; namespace CodexContractsPlugin.ChainMonitor { diff --git a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateRequest.cs b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateRequest.cs index d908d193..408d2746 100644 --- a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateRequest.cs +++ b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/ChainStateRequest.cs @@ -1,6 +1,6 @@ using CodexContractsPlugin.Marketplace; -using GethPlugin; using Logging; +using Utils; namespace CodexContractsPlugin.ChainMonitor { diff --git a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/DoNothingChainEventHandler.cs b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/DoNothingChainEventHandler.cs index 749f30c4..c15b0c7d 100644 --- a/ProjectPlugins/CodexContractsPlugin/ChainMonitor/DoNothingChainEventHandler.cs +++ b/ProjectPlugins/CodexContractsPlugin/ChainMonitor/DoNothingChainEventHandler.cs @@ -1,5 +1,5 @@ -using GethPlugin; -using System.Numerics; +using System.Numerics; +using Utils; namespace CodexContractsPlugin.ChainMonitor { diff --git a/ProjectPlugins/CodexContractsPlugin/ContractInteractions.cs b/ProjectPlugins/CodexContractsPlugin/ContractInteractions.cs index feecd054..289db637 100644 --- a/ProjectPlugins/CodexContractsPlugin/ContractInteractions.cs +++ b/ProjectPlugins/CodexContractsPlugin/ContractInteractions.cs @@ -5,8 +5,8 @@ using Logging; using Nethereum.ABI.FunctionEncoding.Attributes; using Nethereum.Contracts; using Nethereum.Hex.HexConvertors.Extensions; -using NethereumWorkflow; using System.Numerics; +using Utils; namespace CodexContractsPlugin { diff --git a/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs b/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs index b04bf59d..9a311909 100644 --- a/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs +++ b/ProjectPlugins/CodexContractsPlugin/Marketplace/Customizations.cs @@ -1,7 +1,7 @@ #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. using BlockchainUtils; -using GethPlugin; using Newtonsoft.Json; +using Utils; namespace CodexContractsPlugin.Marketplace { diff --git a/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs b/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs deleted file mode 100644 index 719b0282..00000000 --- a/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs +++ /dev/null @@ -1,35 +0,0 @@ -using CodexPlugin.Hooks; -using Core; - -namespace CodexPlugin -{ - public interface ICodexNodeFactory - { - CodexNode CreateOnlineCodexNode(CodexAccess access); - } - - public class CodexNodeFactory : ICodexNodeFactory - { - private readonly IPluginTools tools; - private readonly CodexHooksFactory codexHooksFactory; - - public CodexNodeFactory(IPluginTools tools, CodexHooksFactory codexHooksFactory) - { - this.tools = tools; - this.codexHooksFactory = codexHooksFactory; - } - - public CodexNode CreateOnlineCodexNode(CodexAccess access) - { - var hooks = codexHooksFactory.CreateHooks(access.GetName()); - var marketplaceAccess = GetMarketplaceAccess(access, hooks); - return new CodexNode(tools, access, marketplaceAccess, hooks); - } - - private IMarketplaceAccess GetMarketplaceAccess(CodexAccess codexAccess, ICodexNodeHooks hooks) - { - if (codexAccess.GetEthAccount() == null) return new MarketplaceUnavailable(); - return new MarketplaceAccess(tools.GetLog(), codexAccess, hooks); - } - } -} diff --git a/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs b/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs index be90581a..c178f459 100644 --- a/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs +++ b/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs @@ -1,4 +1,5 @@ -using Core; +using CodexClient; +using Core; using MetricsPlugin; using System.Collections; using Utils; @@ -41,7 +42,7 @@ namespace CodexPlugin public ICodexNode[] Nodes => nodes; public DebugInfoVersion Version { get; private set; } - public Address[] ScrapeTargets => Nodes.Select(n => n.MetricsScrapeTarget).ToArray(); + public Address[] ScrapeTargets => Nodes.Select(n => n.GetMetricsScrapeTarget()).ToArray(); public IEnumerator GetEnumerator() { diff --git a/ProjectPlugins/CodexPlugin/CodexPlugin.cs b/ProjectPlugins/CodexPlugin/CodexPlugin.cs index 04c2d5f5..d47da7c7 100644 --- a/ProjectPlugins/CodexPlugin/CodexPlugin.cs +++ b/ProjectPlugins/CodexPlugin/CodexPlugin.cs @@ -1,4 +1,5 @@ -using CodexPlugin.Hooks; +using CodexClient; +using CodexClient.Hooks; using Core; using KubernetesWorkflow.Types; diff --git a/ProjectPlugins/CodexPlugin/CodexSetup.cs b/ProjectPlugins/CodexPlugin/CodexSetup.cs index b8ef3bb4..d56c10db 100644 --- a/ProjectPlugins/CodexPlugin/CodexSetup.cs +++ b/ProjectPlugins/CodexPlugin/CodexSetup.cs @@ -1,4 +1,5 @@ -using CodexContractsPlugin; +using CodexClient; +using CodexContractsPlugin; using GethPlugin; using KubernetesWorkflow; using Utils; @@ -223,7 +224,7 @@ namespace CodexPlugin { if (pinned) return accounts.Last(); - var a = EthAccount.GenerateNew(); + var a = EthAccountGenerator.GenerateNew(); accounts.Add(a); return a; } diff --git a/ProjectPlugins/MetricsPlugin/MetricsQuery.cs b/ProjectPlugins/MetricsPlugin/MetricsQuery.cs index 7b70bd5c..0f0d11db 100644 --- a/ProjectPlugins/MetricsPlugin/MetricsQuery.cs +++ b/ProjectPlugins/MetricsPlugin/MetricsQuery.cs @@ -4,6 +4,7 @@ using KubernetesWorkflow.Types; using Logging; using System.Globalization; using Utils; +using WebUtils; namespace MetricsPlugin { From 9a227b3d0e6a71e7ed059fa70d662c3320fa5765 Mon Sep 17 00:00:00 2001 From: ThatBen Date: Thu, 16 Jan 2025 13:51:29 +0100 Subject: [PATCH 10/25] lots of using-statement fixes --- .gitignore | 1 + .../CodexClient/CodexNodeFactory.cs | 12 +- ProjectPlugins/CodexPlugin/CodexDeployment.cs | 3 +- .../CodexInstanceContainerExtension.cs | 3 +- ProjectPlugins/CodexPlugin/CodexNodeGroup.cs | 5 +- ProjectPlugins/CodexPlugin/CodexStarter.cs | 135 +++++++++++------- .../CodexPlugin/CodexStartupConfig.cs | 3 +- .../CodexPlugin/CoreInterfaceExtensions.cs | 3 +- .../OverwatchSupport/CodexLogConverter.cs | 4 +- .../CodexNodeTranscriptWriter.cs | 3 +- .../OverwatchSupport/CodexTranscriptWriter.cs | 3 +- .../OverwatchSupport/IdentityMap.cs | 4 +- .../BlockReceivedLineConverter.cs | 4 +- .../LineConverters/BootstrapLineConverter.cs | 4 +- .../DialSuccessfulLineConverter.cs | 4 +- .../PeerDroppedLineConverter.cs | 4 +- .../OverwatchSupport/ModelExtensions.cs | 3 +- ProjectPlugins/CodexPluginPrebuild/Program.cs | 2 +- Tests/DistTestCore/Configuration.cs | 4 +- Tests/DistTestCore/DistTest.cs | 18 ++- Tests/DistTestCore/TestLifecycle.cs | 11 +- .../AutoBootstrapDistTest.cs | 3 +- .../BasicTests/PyramidTests.cs | 7 +- Tests/ExperimentalTests/CodexDistTest.cs | 2 +- .../FullyConnectedDownloadTests.cs | 5 +- .../MultiswarmTests.cs | 2 +- .../Helpers/FullConnectivityHelper.cs | 2 +- .../Helpers/PeerConnectionTestHelpers.cs | 2 +- .../Helpers/PeerDownloadTestHelpers.cs | 2 +- .../LayeredDiscoveryTests.cs | 2 +- .../PeerDiscoveryTests/PeerDiscoveryTests.cs | 3 +- .../UtilityTests/LogHelperTests.cs | 2 +- .../UtilityTests/NetworkIsolationTest.cs | 3 +- .../CodexContractsPlugin/TestTokenTests.cs | 4 +- Tools/AutoClient/CodexInstance.cs | 3 +- Tools/BiblioTech/Commands/MintCommand.cs | 1 + .../Commands/UserAssociateCommand.cs | 10 +- Tools/BiblioTech/Options/EthAddressOption.cs | 4 +- Tools/BiblioTech/UserData.cs | 1 + Tools/BiblioTech/UserRepo.cs | 1 + Tools/KeyMaker/Controllers/KeyController.cs | 4 +- Tools/TestNetRewarder/HistoricState.cs | 2 +- Tools/TestNetRewarder/RequestBuilder.cs | 2 +- Tools/TestNetRewarder/RewardCheck.cs | 3 +- .../Receivers/LogReplaceReceiver.cs | 2 +- .../Receivers/NodesDegree.cs | 2 +- 46 files changed, 178 insertions(+), 129 deletions(-) diff --git a/.gitignore b/.gitignore index 488beaf6..b5c9e08b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ obj bin .vscode Tools/AutoClient/datapath +.editorconfig diff --git a/ProjectPlugins/CodexClient/CodexNodeFactory.cs b/ProjectPlugins/CodexClient/CodexNodeFactory.cs index 447ae018..d8048a0e 100644 --- a/ProjectPlugins/CodexClient/CodexNodeFactory.cs +++ b/ProjectPlugins/CodexClient/CodexNodeFactory.cs @@ -9,15 +9,15 @@ namespace CodexClient { private readonly ILog log; private readonly IFileManager fileManager; - private readonly ICodexHooksProvider hooksProvider; + private readonly CodexHooksFactory hooksFactor; private readonly IHttpFactory httpFactory; private readonly IIProcessControlFactory processControlFactory; - public CodexNodeFactory(ILog log, IFileManager fileManager, ICodexHooksProvider hooksProvider, IHttpFactory httpFactory, IIProcessControlFactory processControlFactory) + public CodexNodeFactory(ILog log, IFileManager fileManager, CodexHooksFactory hooksFactory, IHttpFactory httpFactory, IIProcessControlFactory processControlFactory) { this.log = log; this.fileManager = fileManager; - this.hooksProvider = hooksProvider; + this.hooksFactor = hooksFactory; this.httpFactory = httpFactory; this.processControlFactory = processControlFactory; } @@ -26,9 +26,11 @@ namespace CodexClient { var processControl = processControlFactory.CreateProcessControl(instance); var access = new CodexAccess(log, httpFactory, processControl, instance); - var hooks = hooksProvider.CreateHooks(access.GetName()); + var hooks = hooksFactor.CreateHooks(access.GetName()); var marketplaceAccess = CreateMarketplaceAccess(instance, access, hooks); - return new CodexNode(log, access, fileManager, marketplaceAccess, hooks); + var node = new CodexNode(log, access, fileManager, marketplaceAccess, hooks); + node.Initialize(); + return node; } private IMarketplaceAccess CreateMarketplaceAccess(ICodexInstance instance, CodexAccess access, ICodexNodeHooks hooks) diff --git a/ProjectPlugins/CodexPlugin/CodexDeployment.cs b/ProjectPlugins/CodexPlugin/CodexDeployment.cs index 0a22deae..ec44db3a 100644 --- a/ProjectPlugins/CodexPlugin/CodexDeployment.cs +++ b/ProjectPlugins/CodexPlugin/CodexDeployment.cs @@ -1,4 +1,5 @@ -using CodexContractsPlugin; +using CodexClient; +using CodexContractsPlugin; using GethPlugin; using KubernetesWorkflow.Types; diff --git a/ProjectPlugins/CodexPlugin/CodexInstanceContainerExtension.cs b/ProjectPlugins/CodexPlugin/CodexInstanceContainerExtension.cs index 5e97dd0f..2104823e 100644 --- a/ProjectPlugins/CodexPlugin/CodexInstanceContainerExtension.cs +++ b/ProjectPlugins/CodexPlugin/CodexInstanceContainerExtension.cs @@ -1,6 +1,5 @@ -using Core; +using CodexClient; using KubernetesWorkflow.Types; -using Logging; using Utils; namespace CodexPlugin diff --git a/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs b/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs index c178f459..8c82ce22 100644 --- a/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs +++ b/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs @@ -14,9 +14,9 @@ namespace CodexPlugin public class CodexNodeGroup : ICodexNodeGroup { - private readonly CodexNode[] nodes; + private readonly ICodexNode[] nodes; - public CodexNodeGroup(IPluginTools tools, CodexNode[] nodes) + public CodexNodeGroup(IPluginTools tools, ICodexNode[] nodes) { this.nodes = nodes; Version = new DebugInfoVersion(); @@ -61,7 +61,6 @@ namespace CodexPlugin public void EnsureOnline() { - foreach (var node in nodes) node.Initialize(); var versionResponses = Nodes.Select(n => n.Version); var first = versionResponses.First(); diff --git a/ProjectPlugins/CodexPlugin/CodexStarter.cs b/ProjectPlugins/CodexPlugin/CodexStarter.cs index a2baf96f..e6521479 100644 --- a/ProjectPlugins/CodexPlugin/CodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/CodexStarter.cs @@ -1,4 +1,5 @@ -using CodexPlugin.Hooks; +using CodexClient; +using CodexClient.Hooks; using Core; using KubernetesWorkflow; using KubernetesWorkflow.Types; @@ -7,7 +8,7 @@ using Utils; namespace CodexPlugin { - public class CodexStarter : IProcessControl + public class CodexStarter : IIProcessControlFactory { private readonly IPluginTools pluginTools; private readonly CodexContainerRecipe recipe = new CodexContainerRecipe(); @@ -24,6 +25,15 @@ namespace CodexPlugin public CodexHooksFactory HooksFactory { get; } = new CodexHooksFactory(); + public IProcessControl CreateProcessControl(ICodexInstance instance) + { + var pod = podMap[instance]; + return new CodexContainerProcessControl(pluginTools, pod, onStop: () => + { + podMap.Remove(instance); + }); + } + public RunningPod[] BringOnline(CodexSetup codexSetup) { LogSeparator(); @@ -47,46 +57,14 @@ 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 IDownloadedLog DownloadLog(ICodexInstance instance, LogFile file) - { - var workflow = pluginTools.CreateWorkflow(); - var pod = podMap[instance]; - return workflow.DownloadContainerLog(pod.Containers.Single()); - } - - public void DeleteDataDirFolder(ICodexInstance instance) - { - var pod = podMap[instance]; - var container = pod.Containers.Single(); - - try - { - var dataDirVar = container.Recipe.EnvVars.Single(e => e.Name == "CODEX_DATA_DIR"); - var dataDir = dataDirVar.Value; - var workflow = pluginTools.CreateWorkflow(); - workflow.ExecuteCommand(container, "rm", "-Rfv", $"/codex/{dataDir}/repo"); - Log("Deleted repo folder."); - } - catch (Exception e) - { - Log("Unable to delete repo folder: " + e); - } - } - public ICodexNodeGroup WrapCodexContainers(CoreInterface coreInterface, RunningPod[] containers) { - var codexNodeFactory = new CodexNodeFactory(pluginTools, HooksFactory); + var codexNodeFactory = new CodexNodeFactory( + log: pluginTools.GetLog(), + fileManager: pluginTools.GetFileManager(), + hooksFactory: HooksFactory, + httpFactory: pluginTools, + processControlFactory: this); var group = CreateCodexGroup(coreInterface, containers, codexNodeFactory); @@ -139,8 +117,7 @@ namespace CodexPlugin private CodexNodeGroup CreateCodexGroup(CoreInterface coreInterface, RunningPod[] runningContainers, CodexNodeFactory codexNodeFactory) { var instances = runningContainers.Select(CreateInstance).ToArray(); - var accesses = instances.Select(CreateAccess).ToArray(); - var nodes = accesses.Select(codexNodeFactory.CreateOnlineCodexNode).ToArray(); + var nodes = instances.Select(codexNodeFactory.CreateCodexNode).ToArray(); var group = new CodexNodeGroup(pluginTools, nodes); try @@ -156,18 +133,6 @@ 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 = CodexInstanceContainerExtension.CreateFromPod(pod); @@ -198,4 +163,66 @@ namespace CodexPlugin pluginTools.GetLog().Log(message); } } + + public class CodexContainerProcessControl : IProcessControl + { + private readonly IPluginTools tools; + private readonly RunningPod pod; + private readonly Action onStop; + private readonly ContainerCrashWatcher crashWatcher; + + public CodexContainerProcessControl(IPluginTools tools, RunningPod pod, Action onStop) + { + this.tools = tools; + this.pod = pod; + this.onStop = onStop; + + crashWatcher = tools.CreateWorkflow().CreateCrashWatcher(pod.Containers.Single()); + crashWatcher.Start(); + } + + public void Stop(bool waitTillStopped) + { + Log($"Stopping node..."); + var workflow = tools.CreateWorkflow(); + workflow.Stop(pod, waitTillStopped); + crashWatcher.Stop(); + onStop(); + Log("Stopped."); + } + + public IDownloadedLog DownloadLog(LogFile file) + { + var workflow = tools.CreateWorkflow(); + return workflow.DownloadContainerLog(pod.Containers.Single()); + } + + public void DeleteDataDirFolder() + { + var container = pod.Containers.Single(); + + 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("Deleted repo folder."); + } + catch (Exception e) + { + Log("Unable to delete repo folder: " + e); + } + } + + public bool HasCrashed() + { + return crashWatcher.HasCrashed(); + } + + private void Log(string message) + { + tools.GetLog().Log(message); + } + } } diff --git a/ProjectPlugins/CodexPlugin/CodexStartupConfig.cs b/ProjectPlugins/CodexPlugin/CodexStartupConfig.cs index 63903090..69cdac6e 100644 --- a/ProjectPlugins/CodexPlugin/CodexStartupConfig.cs +++ b/ProjectPlugins/CodexPlugin/CodexStartupConfig.cs @@ -1,4 +1,5 @@ -using KubernetesWorkflow; +using CodexClient; +using KubernetesWorkflow; using Utils; namespace CodexPlugin diff --git a/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs b/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs index 6fb8842e..91783e42 100644 --- a/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs +++ b/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs @@ -1,4 +1,5 @@ -using CodexPlugin.Hooks; +using CodexClient; +using CodexClient.Hooks; using Core; using KubernetesWorkflow.Types; diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexLogConverter.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexLogConverter.cs index 9e55e3a4..71be7873 100644 --- a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexLogConverter.cs +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexLogConverter.cs @@ -1,5 +1,5 @@ -using CodexPlugin.OverwatchSupport.LineConverters; -using KubernetesWorkflow; +using CodexClient; +using CodexPlugin.OverwatchSupport.LineConverters; using Logging; using OverwatchTranscript; using Utils; diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexNodeTranscriptWriter.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexNodeTranscriptWriter.cs index c221806e..346715ca 100644 --- a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexNodeTranscriptWriter.cs +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexNodeTranscriptWriter.cs @@ -1,4 +1,5 @@ -using CodexPlugin.Hooks; +using CodexClient; +using CodexClient.Hooks; using OverwatchTranscript; using Utils; diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexTranscriptWriter.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexTranscriptWriter.cs index 1bfc7cf3..afd8d1a2 100644 --- a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexTranscriptWriter.cs +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexTranscriptWriter.cs @@ -1,5 +1,4 @@ -using CodexPlugin.Hooks; -using KubernetesWorkflow; +using CodexClient.Hooks; using Logging; using OverwatchTranscript; using Utils; diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/IdentityMap.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/IdentityMap.cs index d272e9cd..a0843fd5 100644 --- a/ProjectPlugins/CodexPlugin/OverwatchSupport/IdentityMap.cs +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/IdentityMap.cs @@ -1,4 +1,6 @@ -namespace CodexPlugin.OverwatchSupport +using CodexClient; + +namespace CodexPlugin.OverwatchSupport { public class IdentityMap { diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/BlockReceivedLineConverter.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/BlockReceivedLineConverter.cs index c9259df9..70be0bdd 100644 --- a/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/BlockReceivedLineConverter.cs +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/BlockReceivedLineConverter.cs @@ -1,4 +1,6 @@ -namespace CodexPlugin.OverwatchSupport.LineConverters +using CodexClient; + +namespace CodexPlugin.OverwatchSupport.LineConverters { public class BlockReceivedLineConverter : ILineConverter { diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/BootstrapLineConverter.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/BootstrapLineConverter.cs index 792b6e50..9cec14f0 100644 --- a/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/BootstrapLineConverter.cs +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/BootstrapLineConverter.cs @@ -1,4 +1,6 @@ -namespace CodexPlugin.OverwatchSupport.LineConverters +using CodexClient; + +namespace CodexPlugin.OverwatchSupport.LineConverters { public class BootstrapLineConverter : ILineConverter { diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/DialSuccessfulLineConverter.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/DialSuccessfulLineConverter.cs index 0d9f1a7e..510b8dde 100644 --- a/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/DialSuccessfulLineConverter.cs +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/DialSuccessfulLineConverter.cs @@ -1,4 +1,6 @@ -namespace CodexPlugin.OverwatchSupport.LineConverters +using CodexClient; + +namespace CodexPlugin.OverwatchSupport.LineConverters { public class DialSuccessfulLineConverter : ILineConverter { diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/PeerDroppedLineConverter.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/PeerDroppedLineConverter.cs index 6ec30cd1..858c0f8f 100644 --- a/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/PeerDroppedLineConverter.cs +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/PeerDroppedLineConverter.cs @@ -1,4 +1,6 @@ -namespace CodexPlugin.OverwatchSupport.LineConverters +using CodexClient; + +namespace CodexPlugin.OverwatchSupport.LineConverters { public class PeerDroppedLineConverter : ILineConverter { diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/ModelExtensions.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/ModelExtensions.cs index a15d998a..b839ced2 100644 --- a/ProjectPlugins/CodexPlugin/OverwatchSupport/ModelExtensions.cs +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/ModelExtensions.cs @@ -1,4 +1,5 @@ -using OverwatchTranscript; +using CodexClient; +using OverwatchTranscript; namespace CodexPlugin.OverwatchSupport { diff --git a/ProjectPlugins/CodexPluginPrebuild/Program.cs b/ProjectPlugins/CodexPluginPrebuild/Program.cs index fcdf8b5b..6e9d3b9b 100644 --- a/ProjectPlugins/CodexPluginPrebuild/Program.cs +++ b/ProjectPlugins/CodexPluginPrebuild/Program.cs @@ -15,7 +15,7 @@ public static class Program var pluginRoot = FindCodexPluginFolder(); var clientRoot = FindCodexClientFolder(); Console.WriteLine("Located CodexPlugin: " + pluginRoot); - var openApiFile = Path.Combine(pluginRoot, "openapi.yaml"); + var openApiFile = Path.Combine(clientRoot, "openapi.yaml"); var clientFile = Path.Combine(clientRoot, "obj", "openapiClient.cs"); var targetFile = Path.Combine(pluginRoot, "ApiChecker.cs"); diff --git a/Tests/DistTestCore/Configuration.cs b/Tests/DistTestCore/Configuration.cs index 3fa34b5e..c092a27e 100644 --- a/Tests/DistTestCore/Configuration.cs +++ b/Tests/DistTestCore/Configuration.cs @@ -29,12 +29,12 @@ namespace DistTestCore /// public bool AlwaysDownloadContainerLogs { get; set; } - public KubernetesWorkflow.Configuration GetK8sConfiguration(ITimeSet timeSet, string k8sNamespace) + public KubernetesWorkflow.Configuration GetK8sConfiguration(IK8sTimeSet timeSet, string k8sNamespace) { return GetK8sConfiguration(timeSet, new DoNothingK8sHooks(), k8sNamespace); } - public KubernetesWorkflow.Configuration GetK8sConfiguration(ITimeSet timeSet, IK8sHooks hooks, string k8sNamespace) + public KubernetesWorkflow.Configuration GetK8sConfiguration(IK8sTimeSet timeSet, IK8sHooks hooks, string k8sNamespace) { var config = new KubernetesWorkflow.Configuration( kubeConfigFile: kubeConfigFile, diff --git a/Tests/DistTestCore/DistTest.cs b/Tests/DistTestCore/DistTest.cs index 09330df6..691f19ed 100644 --- a/Tests/DistTestCore/DistTest.cs +++ b/Tests/DistTestCore/DistTest.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using NUnit.Framework.Interfaces; using System.Reflection; using Utils; +using WebUtils; using Assert = NUnit.Framework.Assert; namespace DistTestCore @@ -35,7 +36,7 @@ namespace DistTestCore fixtureLog = new FixtureLog(logConfig, startTime, deployId); statusLog = new StatusLog(logConfig, startTime, "dist-tests", deployId); - globalEntryPoint = new EntryPoint(fixtureLog, configuration.GetK8sConfiguration(new DefaultTimeSet(), TestNamespacePrefix), configuration.GetFileManagerFolder()); + globalEntryPoint = new EntryPoint(fixtureLog, configuration.GetK8sConfiguration(new DefaultK8sTimeSet(), TestNamespacePrefix), configuration.GetFileManagerFolder()); Initialize(fixtureLog); } @@ -194,7 +195,8 @@ namespace DistTestCore var lifecycle = new TestLifecycle( fixtureLog.CreateTestLog(), configuration, - GetTimeSet(), + GetWebCallTimeSet(), + GetK8sTimeSet(), testNamespace, deployId, ShouldWaitForCleanup()); @@ -241,10 +243,16 @@ namespace DistTestCore } } - private ITimeSet GetTimeSet() + private IWebCallTimeSet GetWebCallTimeSet() { - if (ShouldUseLongTimeouts()) return new LongTimeSet(); - return new DefaultTimeSet(); + if (ShouldUseLongTimeouts()) return new LongWebCallTimeSet(); + return new DefaultWebCallTimeSet(); + } + + private IK8sTimeSet GetK8sTimeSet() + { + if (ShouldUseLongTimeouts()) return new LongK8sTimeSet(); + return new DefaultK8sTimeSet(); } private bool ShouldWaitForCleanup() diff --git a/Tests/DistTestCore/TestLifecycle.cs b/Tests/DistTestCore/TestLifecycle.cs index 16a4ecd4..2a11cedb 100644 --- a/Tests/DistTestCore/TestLifecycle.cs +++ b/Tests/DistTestCore/TestLifecycle.cs @@ -6,6 +6,7 @@ using KubernetesWorkflow.Recipe; using KubernetesWorkflow.Types; using Logging; using Utils; +using WebUtils; namespace DistTestCore { @@ -18,15 +19,16 @@ namespace DistTestCore private readonly string deployId; private readonly List stoppedContainerLogs = new List(); - public TestLifecycle(TestLog log, Configuration configuration, ITimeSet timeSet, string testNamespace, string deployId, bool waitForCleanup) + public TestLifecycle(TestLog log, Configuration configuration, IWebCallTimeSet webCallTimeSet, IK8sTimeSet k8sTimeSet, string testNamespace, string deployId, bool waitForCleanup) { Log = log; Configuration = configuration; - TimeSet = timeSet; + WebCallTimeSet = webCallTimeSet; + K8STimeSet = k8sTimeSet; TestNamespace = testNamespace; TestStart = DateTime.UtcNow; - entryPoint = new EntryPoint(log, configuration.GetK8sConfiguration(timeSet, this, testNamespace), configuration.GetFileManagerFolder(), timeSet); + entryPoint = new EntryPoint(log, configuration.GetK8sConfiguration(k8sTimeSet, this, testNamespace), configuration.GetFileManagerFolder(), webCallTimeSet, k8sTimeSet); metadata = entryPoint.GetPluginMetadata(); CoreInterface = entryPoint.CreateInterface(); this.deployId = deployId; @@ -37,7 +39,8 @@ namespace DistTestCore public DateTime TestStart { get; } public TestLog Log { get; } public Configuration Configuration { get; } - public ITimeSet TimeSet { get; } + public IWebCallTimeSet WebCallTimeSet { get; } + public IK8sTimeSet K8STimeSet { get; } public string TestNamespace { get; } public bool WaitForCleanup { get; } public CoreInterface CoreInterface { get; } diff --git a/Tests/ExperimentalTests/AutoBootstrapDistTest.cs b/Tests/ExperimentalTests/AutoBootstrapDistTest.cs index 668f27b5..d39c31ec 100644 --- a/Tests/ExperimentalTests/AutoBootstrapDistTest.cs +++ b/Tests/ExperimentalTests/AutoBootstrapDistTest.cs @@ -1,4 +1,5 @@ -using CodexPlugin; +using CodexClient; +using CodexPlugin; using DistTestCore; using NUnit.Framework; diff --git a/Tests/ExperimentalTests/BasicTests/PyramidTests.cs b/Tests/ExperimentalTests/BasicTests/PyramidTests.cs index 306d9d44..390a6c0e 100644 --- a/Tests/ExperimentalTests/BasicTests/PyramidTests.cs +++ b/Tests/ExperimentalTests/BasicTests/PyramidTests.cs @@ -1,10 +1,5 @@ -using CodexPlugin; +using CodexClient; using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Utils; namespace CodexTests.BasicTests diff --git a/Tests/ExperimentalTests/CodexDistTest.cs b/Tests/ExperimentalTests/CodexDistTest.cs index fbe25ffa..3d83021a 100644 --- a/Tests/ExperimentalTests/CodexDistTest.cs +++ b/Tests/ExperimentalTests/CodexDistTest.cs @@ -1,4 +1,5 @@ using BlockchainUtils; +using CodexClient; using CodexContractsPlugin; using CodexNetDeployer; using CodexPlugin; @@ -15,7 +16,6 @@ using Newtonsoft.Json; using NUnit.Framework; using NUnit.Framework.Constraints; using OverwatchTranscript; -using Utils; namespace CodexTests { diff --git a/Tests/ExperimentalTests/DownloadConnectivityTests/FullyConnectedDownloadTests.cs b/Tests/ExperimentalTests/DownloadConnectivityTests/FullyConnectedDownloadTests.cs index 00dacd22..8898f49c 100644 --- a/Tests/ExperimentalTests/DownloadConnectivityTests/FullyConnectedDownloadTests.cs +++ b/Tests/ExperimentalTests/DownloadConnectivityTests/FullyConnectedDownloadTests.cs @@ -1,6 +1,5 @@ -using CodexContractsPlugin; -using CodexPlugin; -using GethPlugin; +using CodexClient; +using CodexContractsPlugin; using NUnit.Framework; using Utils; diff --git a/Tests/ExperimentalTests/DownloadConnectivityTests/MultiswarmTests.cs b/Tests/ExperimentalTests/DownloadConnectivityTests/MultiswarmTests.cs index 29618947..8b8f9d1e 100644 --- a/Tests/ExperimentalTests/DownloadConnectivityTests/MultiswarmTests.cs +++ b/Tests/ExperimentalTests/DownloadConnectivityTests/MultiswarmTests.cs @@ -1,4 +1,4 @@ -using CodexPlugin; +using CodexClient; using FileUtils; using Logging; using NUnit.Framework; diff --git a/Tests/ExperimentalTests/Helpers/FullConnectivityHelper.cs b/Tests/ExperimentalTests/Helpers/FullConnectivityHelper.cs index 58474d16..f57a3c65 100644 --- a/Tests/ExperimentalTests/Helpers/FullConnectivityHelper.cs +++ b/Tests/ExperimentalTests/Helpers/FullConnectivityHelper.cs @@ -1,4 +1,4 @@ -using CodexPlugin; +using CodexClient; using Logging; using NUnit.Framework; diff --git a/Tests/ExperimentalTests/Helpers/PeerConnectionTestHelpers.cs b/Tests/ExperimentalTests/Helpers/PeerConnectionTestHelpers.cs index 692162b8..ab4bd6a3 100644 --- a/Tests/ExperimentalTests/Helpers/PeerConnectionTestHelpers.cs +++ b/Tests/ExperimentalTests/Helpers/PeerConnectionTestHelpers.cs @@ -1,4 +1,4 @@ -using CodexPlugin; +using CodexClient; using Logging; using static CodexTests.Helpers.FullConnectivityHelper; diff --git a/Tests/ExperimentalTests/Helpers/PeerDownloadTestHelpers.cs b/Tests/ExperimentalTests/Helpers/PeerDownloadTestHelpers.cs index 6dab12a1..9f7d309d 100644 --- a/Tests/ExperimentalTests/Helpers/PeerDownloadTestHelpers.cs +++ b/Tests/ExperimentalTests/Helpers/PeerDownloadTestHelpers.cs @@ -1,4 +1,4 @@ -using CodexPlugin; +using CodexClient; using FileUtils; using Logging; using Utils; diff --git a/Tests/ExperimentalTests/PeerDiscoveryTests/LayeredDiscoveryTests.cs b/Tests/ExperimentalTests/PeerDiscoveryTests/LayeredDiscoveryTests.cs index 33225d6f..dc3d049f 100644 --- a/Tests/ExperimentalTests/PeerDiscoveryTests/LayeredDiscoveryTests.cs +++ b/Tests/ExperimentalTests/PeerDiscoveryTests/LayeredDiscoveryTests.cs @@ -1,4 +1,4 @@ -using CodexPlugin; +using CodexClient; using NUnit.Framework; namespace CodexTests.PeerDiscoveryTests diff --git a/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs b/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs index 6ec35169..88ee0dbb 100644 --- a/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs +++ b/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs @@ -1,6 +1,5 @@ using CodexContractsPlugin; -using CodexPlugin; -using GethPlugin; +using CodexClient; using NUnit.Framework; namespace CodexTests.PeerDiscoveryTests diff --git a/Tests/ExperimentalTests/UtilityTests/LogHelperTests.cs b/Tests/ExperimentalTests/UtilityTests/LogHelperTests.cs index 443201f4..d6a1dfc4 100644 --- a/Tests/ExperimentalTests/UtilityTests/LogHelperTests.cs +++ b/Tests/ExperimentalTests/UtilityTests/LogHelperTests.cs @@ -1,4 +1,4 @@ -using CodexPlugin; +using CodexClient; using NUnit.Framework; using Utils; diff --git a/Tests/ExperimentalTests/UtilityTests/NetworkIsolationTest.cs b/Tests/ExperimentalTests/UtilityTests/NetworkIsolationTest.cs index c100143c..a86f6260 100644 --- a/Tests/ExperimentalTests/UtilityTests/NetworkIsolationTest.cs +++ b/Tests/ExperimentalTests/UtilityTests/NetworkIsolationTest.cs @@ -1,4 +1,5 @@ -using CodexPlugin; +using CodexClient; +using CodexPlugin; using DistTestCore; using NUnit.Framework; using Utils; diff --git a/Tests/FrameworkTests/CodexContractsPlugin/TestTokenTests.cs b/Tests/FrameworkTests/CodexContractsPlugin/TestTokenTests.cs index b74ac86e..3dadf6f2 100644 --- a/Tests/FrameworkTests/CodexContractsPlugin/TestTokenTests.cs +++ b/Tests/FrameworkTests/CodexContractsPlugin/TestTokenTests.cs @@ -1,6 +1,6 @@ -using CodexContractsPlugin; -using NUnit.Framework; +using NUnit.Framework; using System.Numerics; +using Utils; namespace FrameworkTests.CodexContractsPlugin { diff --git a/Tools/AutoClient/CodexInstance.cs b/Tools/AutoClient/CodexInstance.cs index 5399b2b1..3be59d45 100644 --- a/Tools/AutoClient/CodexInstance.cs +++ b/Tools/AutoClient/CodexInstance.cs @@ -1,4 +1,5 @@ -using CodexOpenApi; +using CodexClient; +using CodexOpenApi; using CodexPlugin; using Logging; using Nethereum.Model; diff --git a/Tools/BiblioTech/Commands/MintCommand.cs b/Tools/BiblioTech/Commands/MintCommand.cs index b5cb9330..8a6cd33c 100644 --- a/Tools/BiblioTech/Commands/MintCommand.cs +++ b/Tools/BiblioTech/Commands/MintCommand.cs @@ -1,6 +1,7 @@ using BiblioTech.Options; using CodexContractsPlugin; using GethPlugin; +using Utils; namespace BiblioTech.Commands { diff --git a/Tools/BiblioTech/Commands/UserAssociateCommand.cs b/Tools/BiblioTech/Commands/UserAssociateCommand.cs index 1fc6d63c..61d55fb9 100644 --- a/Tools/BiblioTech/Commands/UserAssociateCommand.cs +++ b/Tools/BiblioTech/Commands/UserAssociateCommand.cs @@ -1,8 +1,6 @@ using BiblioTech.Options; using Discord; -using GethPlugin; -using k8s.KubeConfigModels; -using NBitcoin.Secp256k1; +using Utils; namespace BiblioTech.Commands { @@ -67,14 +65,14 @@ namespace BiblioTech.Commands await Program.AdminChecker.SendInAdminChannel($"User {Mention(user)} used '/{Name}' but the provided address is already in use by another user. (address: {newAddress})"); } - private async Task ResponseOK(CommandContext context, IUser user, GethPlugin.EthAddress newAddress) + private async Task ResponseOK(CommandContext context, IUser user, EthAddress newAddress) { await context.Followup(new string[] -{ + { "Done! Thank you for joining the test net!", "By default, the bot will @-mention you with test-net related notifications.", $"You can enable/disable this behavior with the '/{notifyCommand.Name}' command." -}); + }); await Program.AdminChecker.SendInAdminChannel($"User {Mention(user)} used '/{Name}' successfully. ({newAddress})"); } diff --git a/Tools/BiblioTech/Options/EthAddressOption.cs b/Tools/BiblioTech/Options/EthAddressOption.cs index c8cc0a78..0351972a 100644 --- a/Tools/BiblioTech/Options/EthAddressOption.cs +++ b/Tools/BiblioTech/Options/EthAddressOption.cs @@ -1,5 +1,5 @@ -using GethPlugin; -using Nethereum.Util; +using Nethereum.Util; +using Utils; namespace BiblioTech.Options { diff --git a/Tools/BiblioTech/UserData.cs b/Tools/BiblioTech/UserData.cs index 540c326a..715611e0 100644 --- a/Tools/BiblioTech/UserData.cs +++ b/Tools/BiblioTech/UserData.cs @@ -1,5 +1,6 @@ using CodexContractsPlugin; using GethPlugin; +using Utils; namespace BiblioTech { diff --git a/Tools/BiblioTech/UserRepo.cs b/Tools/BiblioTech/UserRepo.cs index 35d8ec87..d1a766ab 100644 --- a/Tools/BiblioTech/UserRepo.cs +++ b/Tools/BiblioTech/UserRepo.cs @@ -2,6 +2,7 @@ using Discord; using GethPlugin; using Newtonsoft.Json; +using Utils; namespace BiblioTech { diff --git a/Tools/KeyMaker/Controllers/KeyController.cs b/Tools/KeyMaker/Controllers/KeyController.cs index 09644e9c..c4d1890d 100644 --- a/Tools/KeyMaker/Controllers/KeyController.cs +++ b/Tools/KeyMaker/Controllers/KeyController.cs @@ -1,5 +1,5 @@ +using GethPlugin; using Microsoft.AspNetCore.Mvc; -using Utils; namespace KeyMaker.Controllers { @@ -10,7 +10,7 @@ namespace KeyMaker.Controllers [HttpGet] public KeyResponse Get() { - var account = EthAccount.GenerateNew(); + var account = EthAccountGenerator.GenerateNew(); return new KeyResponse { diff --git a/Tools/TestNetRewarder/HistoricState.cs b/Tools/TestNetRewarder/HistoricState.cs index 8487aa06..2e744145 100644 --- a/Tools/TestNetRewarder/HistoricState.cs +++ b/Tools/TestNetRewarder/HistoricState.cs @@ -1,7 +1,7 @@ using CodexContractsPlugin; using CodexContractsPlugin.Marketplace; -using GethPlugin; using Newtonsoft.Json; +using Utils; namespace TestNetRewarder { diff --git a/Tools/TestNetRewarder/RequestBuilder.cs b/Tools/TestNetRewarder/RequestBuilder.cs index 6ea25f34..b1f641b8 100644 --- a/Tools/TestNetRewarder/RequestBuilder.cs +++ b/Tools/TestNetRewarder/RequestBuilder.cs @@ -1,5 +1,5 @@ using DiscordRewards; -using GethPlugin; +using Utils; namespace TestNetRewarder { diff --git a/Tools/TestNetRewarder/RewardCheck.cs b/Tools/TestNetRewarder/RewardCheck.cs index b7f6b73b..3739d686 100644 --- a/Tools/TestNetRewarder/RewardCheck.cs +++ b/Tools/TestNetRewarder/RewardCheck.cs @@ -1,9 +1,8 @@ using BlockchainUtils; using CodexContractsPlugin.ChainMonitor; using DiscordRewards; -using GethPlugin; -using NethereumWorkflow; using System.Numerics; +using Utils; namespace TestNetRewarder { diff --git a/Tools/TranscriptAnalysis/Receivers/LogReplaceReceiver.cs b/Tools/TranscriptAnalysis/Receivers/LogReplaceReceiver.cs index d4963eef..a20f656c 100644 --- a/Tools/TranscriptAnalysis/Receivers/LogReplaceReceiver.cs +++ b/Tools/TranscriptAnalysis/Receivers/LogReplaceReceiver.cs @@ -1,4 +1,4 @@ -using CodexPlugin; +using CodexClient; using CodexPlugin.OverwatchSupport; using OverwatchTranscript; diff --git a/Tools/TranscriptAnalysis/Receivers/NodesDegree.cs b/Tools/TranscriptAnalysis/Receivers/NodesDegree.cs index d3ab71a0..d37b3d38 100644 --- a/Tools/TranscriptAnalysis/Receivers/NodesDegree.cs +++ b/Tools/TranscriptAnalysis/Receivers/NodesDegree.cs @@ -1,4 +1,4 @@ -using CodexPlugin; +using CodexClient; using CodexPlugin.OverwatchSupport; using OverwatchTranscript; From 1a8b7b79ef40aeacd2f3ee2b282aafb4602e2286 Mon Sep 17 00:00:00 2001 From: ThatBen Date: Thu, 16 Jan 2025 15:13:16 +0100 Subject: [PATCH 11/25] wip --- Framework/FileUtils/TrackedFile.cs | 6 + Framework/Utils/Address.cs | 16 ++ ProjectPlugins/CodexClient/CodexAccess.cs | 3 +- ProjectPlugins/CodexClient/CodexInstance.cs | 14 ++ ProjectPlugins/CodexClient/CodexNode.cs | 9 +- .../CodexClient/CodexNodeFactory.cs | 15 +- ProjectPlugins/CodexClient/ProcessControl.cs | 31 +++- .../CodexClient/StoragePurchaseContract.cs | 5 +- ProjectPlugins/CodexPlugin/CodexNodeGroup.cs | 7 +- ProjectPlugins/CodexPlugin/CodexStarter.cs | 2 +- .../MetricsPlugin/CoreInterfaceExtensions.cs | 8 +- .../MetricsPlugin/MetricsScrapeTarget.cs | 14 -- Tests/CodexContinuousTests/ContinuousTest.cs | 8 +- .../ElasticSearchLogDownloader.cs | 1 + Tests/CodexContinuousTests/NodeRunner.cs | 3 +- Tests/CodexContinuousTests/SingleTestRun.cs | 1 + Tests/CodexContinuousTests/StartupChecker.cs | 3 +- .../Tests/HoldMyBeerTest.cs | 2 +- Tests/CodexContinuousTests/Tests/PeersTest.cs | 2 +- .../Tests/TwoClientTest.cs | 2 +- .../BasicTests/DownloadTests.cs | 2 +- .../CodexLongTests/BasicTests/UploadTests.cs | 2 +- .../MultiPeerDownloadTests.cs | 2 +- .../ScalabilityTests/ScalabilityTests.cs | 6 +- .../DataTests/InterruptUploadTest.cs | 2 +- .../DataTests/OneClientTest.cs | 7 +- .../CodexReleaseTests/DataTests/SwarmTest.cs | 3 +- .../DataTests/TwoClientTest.cs | 3 +- .../DataTests/UnknownCidTest.cs | 7 +- .../MarketTests/ContractFailedTest.cs | 9 +- .../MarketTests/ContractSuccessfulTest.cs | 5 +- .../MarketplaceAutoBootstrapDistTest.cs | 5 +- .../MarketTests/MultipleContractsTest.cs | 5 +- .../NodeTests/PeerTableTests.cs | 8 +- .../BasicTests/ExampleTests.cs | 3 +- .../BasicTests/MarketplaceTests.cs | 2 +- .../PeerDiscoveryTests/PeerDiscoveryTests.cs | 1 + .../UtilityTests/DiscordBotTests.cs | 4 +- Tools/AutoClient/App.cs | 6 +- Tools/AutoClient/AutomaticPurchaser.cs | 40 ++--- Tools/AutoClient/CidRepo.cs | 86 ---------- Tools/AutoClient/CodexContext.cs | 32 ++++ Tools/AutoClient/CodexInstance.cs | 162 ------------------ Tools/AutoClient/CodexWrapper.cs | 45 +++++ .../Modes/FolderStore/FileWorker.cs | 53 +++--- .../Modes/FolderStore/FolderWorkOverview.cs | 18 +- Tools/AutoClient/Modes/FolderStoreMode.cs | 16 +- Tools/AutoClient/Modes/Mode.cs | 10 +- Tools/AutoClient/Modes/PurchasingMode.cs | 4 +- Tools/AutoClient/Program.cs | 47 ++--- Tools/BiblioTech/CodexCidChecker.cs | 57 +++--- Tools/BiblioTech/Commands/CheckCidCommand.cs | 7 +- .../BiblioTech/Commands/GetBalanceCommand.cs | 1 + Tools/BiblioTech/Rewards/RoleDriver.cs | 3 +- Tools/CodexNetDeployer/CodexNodeStarter.cs | 3 +- Tools/CodexNetDeployer/Configuration.cs | 2 +- Tools/CodexNetDeployer/Deployer.cs | 16 +- 57 files changed, 317 insertions(+), 519 deletions(-) delete mode 100644 ProjectPlugins/MetricsPlugin/MetricsScrapeTarget.cs delete mode 100644 Tools/AutoClient/CidRepo.cs create mode 100644 Tools/AutoClient/CodexContext.cs delete mode 100644 Tools/AutoClient/CodexInstance.cs create mode 100644 Tools/AutoClient/CodexWrapper.cs diff --git a/Framework/FileUtils/TrackedFile.cs b/Framework/FileUtils/TrackedFile.cs index 9ff45d7a..e60bb760 100644 --- a/Framework/FileUtils/TrackedFile.cs +++ b/Framework/FileUtils/TrackedFile.cs @@ -14,6 +14,12 @@ namespace FileUtils Label = label; } + public static TrackedFile FromPath(ILog log, string filepath) + { + // todo: I don't wanne have to do this to call upload. + return new TrackedFile(log, filepath, string.Empty); + } + public string Filename { get; } public string Label { get; } diff --git a/Framework/Utils/Address.cs b/Framework/Utils/Address.cs index 58b8f2ac..010822b9 100644 --- a/Framework/Utils/Address.cs +++ b/Framework/Utils/Address.cs @@ -22,5 +22,21 @@ { return !string.IsNullOrEmpty(Host) && Port > 0; } + + public static Address Empty() + { + return new Address(string.Empty, string.Empty, 0); + } } + + public interface IHasMetricsScrapeTarget + { + Address GetMetricsScrapeTarget(); + } + + public interface IHasManyMetricScrapeTargets + { + Address[] GetMetricsScrapeTargets(); + } + } diff --git a/ProjectPlugins/CodexClient/CodexAccess.cs b/ProjectPlugins/CodexClient/CodexAccess.cs index 434d7946..c7c3dc32 100644 --- a/ProjectPlugins/CodexClient/CodexAccess.cs +++ b/ProjectPlugins/CodexClient/CodexAccess.cs @@ -1,5 +1,4 @@ -using CodexOpenApi; -using Logging; +using Logging; using Newtonsoft.Json; using Utils; using WebUtils; diff --git a/ProjectPlugins/CodexClient/CodexInstance.cs b/ProjectPlugins/CodexClient/CodexInstance.cs index 54c2e9c3..4f77776b 100644 --- a/ProjectPlugins/CodexClient/CodexInstance.cs +++ b/ProjectPlugins/CodexClient/CodexInstance.cs @@ -36,5 +36,19 @@ namespace CodexClient public Address ListenEndpoint { get; } public EthAccount? EthAccount { get; } public Address? MetricsEndpoint { get; } + + public static ICodexInstance CreateFromApiEndpoint(string name, Address apiEndpoint) + { + return new CodexInstance( + name, + imageName: "-", + startUtc: DateTime.UtcNow, + discoveryEndpoint: Address.Empty(), + apiEndpoint: apiEndpoint, + listenEndpoint: Address.Empty(), + ethAccount: null, + metricsEndpoint: null + ); + } } } diff --git a/ProjectPlugins/CodexClient/CodexNode.cs b/ProjectPlugins/CodexClient/CodexNode.cs index 45cfcd7b..5cdf7b02 100644 --- a/ProjectPlugins/CodexClient/CodexNode.cs +++ b/ProjectPlugins/CodexClient/CodexNode.cs @@ -5,7 +5,7 @@ using Utils; namespace CodexClient { - public partial interface ICodexNode : IHasEthAddress + public partial interface ICodexNode : IHasEthAddress, IHasMetricsScrapeTarget { string GetName(); string GetImageName(); @@ -30,11 +30,11 @@ namespace CodexClient IMarketplaceAccess Marketplace { get; } ITransferSpeeds TransferSpeeds { get; } EthAccount EthAccount { get; } + StoragePurchase GetPurchaseStatus(string purchaseId); Address GetDiscoveryEndpoint(); Address GetApiEndpoint(); Address GetListenEndpoint(); - Address GetMetricsScrapeTarget(); /// /// Warning! The node is not usable after this. @@ -86,6 +86,11 @@ namespace CodexClient public DebugInfoVersion Version { get; private set; } public ITransferSpeeds TransferSpeeds { get => transferSpeeds; } + public StoragePurchase GetPurchaseStatus(string purchaseId) + { + return codexAccess.GetPurchaseStatus(purchaseId); + } + public EthAddress EthAddress { get diff --git a/ProjectPlugins/CodexClient/CodexNodeFactory.cs b/ProjectPlugins/CodexClient/CodexNodeFactory.cs index d8048a0e..22e7a69e 100644 --- a/ProjectPlugins/CodexClient/CodexNodeFactory.cs +++ b/ProjectPlugins/CodexClient/CodexNodeFactory.cs @@ -9,24 +9,29 @@ namespace CodexClient { private readonly ILog log; private readonly IFileManager fileManager; - private readonly CodexHooksFactory hooksFactor; + private readonly CodexHooksFactory hooksFactory; private readonly IHttpFactory httpFactory; - private readonly IIProcessControlFactory processControlFactory; + private readonly IProcessControlFactory processControlFactory; - public CodexNodeFactory(ILog log, IFileManager fileManager, CodexHooksFactory hooksFactory, IHttpFactory httpFactory, IIProcessControlFactory processControlFactory) + public CodexNodeFactory(ILog log, IFileManager fileManager, CodexHooksFactory hooksFactory, IHttpFactory httpFactory, IProcessControlFactory processControlFactory) { this.log = log; this.fileManager = fileManager; - this.hooksFactor = hooksFactory; + this.hooksFactory = hooksFactory; this.httpFactory = httpFactory; this.processControlFactory = processControlFactory; } + public CodexNodeFactory(ILog log, string dataDir) + : this(log, new FileManager(log, dataDir), new CodexHooksFactory(), new HttpFactory(log), new DoNothingProcessControlFactory()) + { + } + public ICodexNode CreateCodexNode(ICodexInstance instance) { var processControl = processControlFactory.CreateProcessControl(instance); var access = new CodexAccess(log, httpFactory, processControl, instance); - var hooks = hooksFactor.CreateHooks(access.GetName()); + var hooks = hooksFactory.CreateHooks(access.GetName()); var marketplaceAccess = CreateMarketplaceAccess(instance, access, hooks); var node = new CodexNode(log, access, fileManager, marketplaceAccess, hooks); node.Initialize(); diff --git a/ProjectPlugins/CodexClient/ProcessControl.cs b/ProjectPlugins/CodexClient/ProcessControl.cs index f431f58b..2ddfab2e 100644 --- a/ProjectPlugins/CodexClient/ProcessControl.cs +++ b/ProjectPlugins/CodexClient/ProcessControl.cs @@ -2,7 +2,7 @@ namespace CodexClient { - public interface IIProcessControlFactory + public interface IProcessControlFactory { IProcessControl CreateProcessControl(ICodexInstance instance); } @@ -14,4 +14,33 @@ namespace CodexClient void DeleteDataDirFolder(); bool HasCrashed(); } + + public class DoNothingProcessControlFactory : IProcessControlFactory + { + public IProcessControl CreateProcessControl(ICodexInstance instance) + { + return new DoNothingProcessControl(); + } + } + + public class DoNothingProcessControl : IProcessControl + { + public void DeleteDataDirFolder() + { + } + + public IDownloadedLog DownloadLog(LogFile file) + { + throw new NotImplementedException("Not supported by DoNothingProcessControl"); + } + + public bool HasCrashed() + { + return false; + } + + public void Stop(bool waitTillStopped) + { + } + } } diff --git a/ProjectPlugins/CodexClient/StoragePurchaseContract.cs b/ProjectPlugins/CodexClient/StoragePurchaseContract.cs index 7e4f0d00..38084eef 100644 --- a/ProjectPlugins/CodexClient/StoragePurchaseContract.cs +++ b/ProjectPlugins/CodexClient/StoragePurchaseContract.cs @@ -14,6 +14,7 @@ namespace CodexClient void WaitForStorageContractStarted(); void WaitForStorageContractFinished(); void WaitForContractFailed(); + StoragePurchase GetPurchaseStatus(); } public class StoragePurchaseContract : IStoragePurchaseContract @@ -88,9 +89,9 @@ namespace CodexClient WaitForStorageContractState(timeout, "failed"); } - public StoragePurchase GetPurchaseStatus(string purchaseId) + public StoragePurchase GetPurchaseStatus() { - return codexAccess.GetPurchaseStatus(purchaseId); + return codexAccess.GetPurchaseStatus(PurchaseId); } private void WaitForStorageContractState(TimeSpan timeout, string desiredState, int sleep = 1000) diff --git a/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs b/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs index 8c82ce22..4a8bd9bb 100644 --- a/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs +++ b/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs @@ -1,6 +1,5 @@ using CodexClient; using Core; -using MetricsPlugin; using System.Collections; using Utils; @@ -42,7 +41,11 @@ namespace CodexPlugin public ICodexNode[] Nodes => nodes; public DebugInfoVersion Version { get; private set; } - public Address[] ScrapeTargets => Nodes.Select(n => n.GetMetricsScrapeTarget()).ToArray(); + + public Address[] GetMetricsScrapeTargets() + { + return Nodes.Select(n => n.GetMetricsScrapeTarget()).ToArray(); + } public IEnumerator GetEnumerator() { diff --git a/ProjectPlugins/CodexPlugin/CodexStarter.cs b/ProjectPlugins/CodexPlugin/CodexStarter.cs index e6521479..1e74a13e 100644 --- a/ProjectPlugins/CodexPlugin/CodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/CodexStarter.cs @@ -8,7 +8,7 @@ using Utils; namespace CodexPlugin { - public class CodexStarter : IIProcessControlFactory + public class CodexStarter : IProcessControlFactory { private readonly IPluginTools pluginTools; private readonly CodexContainerRecipe recipe = new CodexContainerRecipe(); diff --git a/ProjectPlugins/MetricsPlugin/CoreInterfaceExtensions.cs b/ProjectPlugins/MetricsPlugin/CoreInterfaceExtensions.cs index e632e288..76f6c108 100644 --- a/ProjectPlugins/MetricsPlugin/CoreInterfaceExtensions.cs +++ b/ProjectPlugins/MetricsPlugin/CoreInterfaceExtensions.cs @@ -9,7 +9,7 @@ namespace MetricsPlugin { public static RunningPod DeployMetricsCollector(this CoreInterface ci, TimeSpan scrapeInterval, params IHasMetricsScrapeTarget[] scrapeTargets) { - return Plugin(ci).DeployMetricsCollector(scrapeTargets.Select(t => t.MetricsScrapeTarget).ToArray(), scrapeInterval); + return Plugin(ci).DeployMetricsCollector(scrapeTargets.Select(t => t.GetMetricsScrapeTarget()).ToArray(), scrapeInterval); } public static RunningPod DeployMetricsCollector(this CoreInterface ci, TimeSpan scrapeInterval, params Address[] scrapeTargets) @@ -19,7 +19,7 @@ namespace MetricsPlugin public static IMetricsAccess WrapMetricsCollector(this CoreInterface ci, RunningPod metricsPod, IHasMetricsScrapeTarget scrapeTarget) { - return ci.WrapMetricsCollector(metricsPod, scrapeTarget.MetricsScrapeTarget); + return ci.WrapMetricsCollector(metricsPod, scrapeTarget.GetMetricsScrapeTarget()); } public static IMetricsAccess WrapMetricsCollector(this CoreInterface ci, RunningPod metricsPod, Address scrapeTarget) @@ -29,12 +29,12 @@ namespace MetricsPlugin public static IMetricsAccess[] GetMetricsFor(this CoreInterface ci, TimeSpan scrapeInterval, params IHasManyMetricScrapeTargets[] manyScrapeTargets) { - return ci.GetMetricsFor(scrapeInterval, manyScrapeTargets.SelectMany(t => t.ScrapeTargets).ToArray()); + return ci.GetMetricsFor(scrapeInterval, manyScrapeTargets.SelectMany(t => t.GetMetricsScrapeTargets()).ToArray()); } public static IMetricsAccess[] GetMetricsFor(this CoreInterface ci, TimeSpan scrapeInterval, params IHasMetricsScrapeTarget[] scrapeTargets) { - return ci.GetMetricsFor(scrapeInterval, scrapeTargets.Select(t => t.MetricsScrapeTarget).ToArray()); + return ci.GetMetricsFor(scrapeInterval, scrapeTargets.Select(t => t.GetMetricsScrapeTarget()).ToArray()); } public static IMetricsAccess[] GetMetricsFor(this CoreInterface ci, TimeSpan scrapeInterval, params Address[] scrapeTargets) diff --git a/ProjectPlugins/MetricsPlugin/MetricsScrapeTarget.cs b/ProjectPlugins/MetricsPlugin/MetricsScrapeTarget.cs deleted file mode 100644 index 5f497de4..00000000 --- a/ProjectPlugins/MetricsPlugin/MetricsScrapeTarget.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Utils; - -namespace MetricsPlugin -{ - public interface IHasMetricsScrapeTarget - { - Address MetricsScrapeTarget { get; } - } - - public interface IHasManyMetricScrapeTargets - { - Address[] ScrapeTargets { get; } - } -} diff --git a/Tests/CodexContinuousTests/ContinuousTest.cs b/Tests/CodexContinuousTests/ContinuousTest.cs index 00a4c3a7..fa39255c 100644 --- a/Tests/CodexContinuousTests/ContinuousTest.cs +++ b/Tests/CodexContinuousTests/ContinuousTest.cs @@ -1,6 +1,4 @@ -using CodexPlugin; -using Core; -using DistTestCore; +using CodexClient; using FileUtils; using Logging; using MetricsPlugin; @@ -10,7 +8,6 @@ namespace ContinuousTests { public abstract class ContinuousTestLongTimeouts : ContinuousTest { - public override ITimeSet TimeSet => new LongTimeSet(); } public abstract class ContinuousTest @@ -45,13 +42,12 @@ namespace ContinuousTests public ILog Log { get; private set; } = null!; public IFileManager FileManager { get; private set; } = null!; public Configuration Configuration { get; private set; } = null!; - public virtual ITimeSet TimeSet { get { return new DefaultTimeSet(); } } public CancellationToken CancelToken { get; private set; } = new CancellationToken(); public NodeRunner NodeRunner { get; private set; } = null!; public IMetricsAccess CreateMetricsAccess(IHasMetricsScrapeTarget target) { - return CreateMetricsAccess(target.MetricsScrapeTarget); + return CreateMetricsAccess(target.GetMetricsScrapeTarget()); } public IMetricsAccess CreateMetricsAccess(Address target) diff --git a/Tests/CodexContinuousTests/ElasticSearchLogDownloader.cs b/Tests/CodexContinuousTests/ElasticSearchLogDownloader.cs index 7d491939..e198650c 100644 --- a/Tests/CodexContinuousTests/ElasticSearchLogDownloader.cs +++ b/Tests/CodexContinuousTests/ElasticSearchLogDownloader.cs @@ -2,6 +2,7 @@ using KubernetesWorkflow.Types; using Logging; using Utils; +using WebUtils; namespace ContinuousTests { diff --git a/Tests/CodexContinuousTests/NodeRunner.cs b/Tests/CodexContinuousTests/NodeRunner.cs index b51f6222..f2b3c64f 100644 --- a/Tests/CodexContinuousTests/NodeRunner.cs +++ b/Tests/CodexContinuousTests/NodeRunner.cs @@ -3,8 +3,7 @@ using Logging; using Utils; using Core; using CodexPlugin; -using KubernetesWorkflow.Types; -using KubernetesWorkflow; +using CodexClient; namespace ContinuousTests { diff --git a/Tests/CodexContinuousTests/SingleTestRun.cs b/Tests/CodexContinuousTests/SingleTestRun.cs index 6617ae5f..643f472e 100644 --- a/Tests/CodexContinuousTests/SingleTestRun.cs +++ b/Tests/CodexContinuousTests/SingleTestRun.cs @@ -7,6 +7,7 @@ using DistTestCore.Logs; using Core; using KubernetesWorkflow.Types; using TaskFactory = Utils.TaskFactory; +using CodexClient; namespace ContinuousTests { diff --git a/Tests/CodexContinuousTests/StartupChecker.cs b/Tests/CodexContinuousTests/StartupChecker.cs index ab3e637d..0914b3d1 100644 --- a/Tests/CodexContinuousTests/StartupChecker.cs +++ b/Tests/CodexContinuousTests/StartupChecker.cs @@ -1,8 +1,7 @@ -using CodexPlugin; +using CodexClient; using Core; using DistTestCore.Logs; using Logging; -using Newtonsoft.Json; namespace ContinuousTests { diff --git a/Tests/CodexContinuousTests/Tests/HoldMyBeerTest.cs b/Tests/CodexContinuousTests/Tests/HoldMyBeerTest.cs index 76457e2e..6eaec611 100644 --- a/Tests/CodexContinuousTests/Tests/HoldMyBeerTest.cs +++ b/Tests/CodexContinuousTests/Tests/HoldMyBeerTest.cs @@ -1,4 +1,4 @@ -using CodexPlugin; +using CodexClient; using FileUtils; using NUnit.Framework; using Utils; diff --git a/Tests/CodexContinuousTests/Tests/PeersTest.cs b/Tests/CodexContinuousTests/Tests/PeersTest.cs index 87bcb825..94a3a090 100644 --- a/Tests/CodexContinuousTests/Tests/PeersTest.cs +++ b/Tests/CodexContinuousTests/Tests/PeersTest.cs @@ -1,4 +1,4 @@ -using CodexPlugin; +using CodexClient; using CodexTests.Helpers; using ContinuousTests; using NUnit.Framework; diff --git a/Tests/CodexContinuousTests/Tests/TwoClientTest.cs b/Tests/CodexContinuousTests/Tests/TwoClientTest.cs index 190cbb61..4a5f7b5a 100644 --- a/Tests/CodexContinuousTests/Tests/TwoClientTest.cs +++ b/Tests/CodexContinuousTests/Tests/TwoClientTest.cs @@ -1,4 +1,4 @@ -using CodexPlugin; +using CodexClient; using FileUtils; using Logging; using NUnit.Framework; diff --git a/Tests/CodexLongTests/BasicTests/DownloadTests.cs b/Tests/CodexLongTests/BasicTests/DownloadTests.cs index 0a95a643..f77b22e5 100644 --- a/Tests/CodexLongTests/BasicTests/DownloadTests.cs +++ b/Tests/CodexLongTests/BasicTests/DownloadTests.cs @@ -1,4 +1,4 @@ -using CodexPlugin; +using CodexClient; using CodexTests; using DistTestCore; using FileUtils; diff --git a/Tests/CodexLongTests/BasicTests/UploadTests.cs b/Tests/CodexLongTests/BasicTests/UploadTests.cs index 361c3a73..aaa9f1fd 100644 --- a/Tests/CodexLongTests/BasicTests/UploadTests.cs +++ b/Tests/CodexLongTests/BasicTests/UploadTests.cs @@ -1,4 +1,4 @@ -using CodexPlugin; +using CodexClient; using CodexTests; using DistTestCore; using FileUtils; diff --git a/Tests/CodexLongTests/ScalabilityTests/MultiPeerDownloadTests.cs b/Tests/CodexLongTests/ScalabilityTests/MultiPeerDownloadTests.cs index fabf51e0..d4597acb 100644 --- a/Tests/CodexLongTests/ScalabilityTests/MultiPeerDownloadTests.cs +++ b/Tests/CodexLongTests/ScalabilityTests/MultiPeerDownloadTests.cs @@ -1,4 +1,4 @@ -using CodexPlugin; +using CodexClient; using DistTestCore; using NUnit.Framework; using Utils; diff --git a/Tests/CodexLongTests/ScalabilityTests/ScalabilityTests.cs b/Tests/CodexLongTests/ScalabilityTests/ScalabilityTests.cs index a3f05970..dd92de84 100644 --- a/Tests/CodexLongTests/ScalabilityTests/ScalabilityTests.cs +++ b/Tests/CodexLongTests/ScalabilityTests/ScalabilityTests.cs @@ -1,4 +1,4 @@ -using CodexPlugin; +using CodexClient; using DistTestCore; using FileUtils; using NUnit.Framework; @@ -38,11 +38,11 @@ public class ScalabilityTests : CodexDistTest var testFile = GenerateTestFile(fileSizeInMb.MB()); LogNodeStatus(uploader); - var contentId = uploader.UploadFile(testFile, f => LogNodeStatus(uploader)); + var contentId = uploader.UploadFile(testFile); LogNodeStatus(uploader); LogNodeStatus(downloader); - var downloadedFile = downloader.DownloadContent(contentId, f => LogNodeStatus(downloader)); + var downloadedFile = downloader.DownloadContent(contentId); LogNodeStatus(downloader); downloadedFile!.AssertIsEqual(testFile); diff --git a/Tests/CodexReleaseTests/DataTests/InterruptUploadTest.cs b/Tests/CodexReleaseTests/DataTests/InterruptUploadTest.cs index 06b2009c..a4fb8192 100644 --- a/Tests/CodexReleaseTests/DataTests/InterruptUploadTest.cs +++ b/Tests/CodexReleaseTests/DataTests/InterruptUploadTest.cs @@ -1,4 +1,4 @@ -using CodexPlugin; +using CodexClient; using CodexTests; using FileUtils; using NUnit.Framework; diff --git a/Tests/CodexReleaseTests/DataTests/OneClientTest.cs b/Tests/CodexReleaseTests/DataTests/OneClientTest.cs index 1be82b4d..2abf009a 100644 --- a/Tests/CodexReleaseTests/DataTests/OneClientTest.cs +++ b/Tests/CodexReleaseTests/DataTests/OneClientTest.cs @@ -1,11 +1,6 @@ -using CodexPlugin; +using CodexClient; using CodexTests; using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Utils; namespace CodexReleaseTests.DataTests diff --git a/Tests/CodexReleaseTests/DataTests/SwarmTest.cs b/Tests/CodexReleaseTests/DataTests/SwarmTest.cs index 3325d35d..427085a5 100644 --- a/Tests/CodexReleaseTests/DataTests/SwarmTest.cs +++ b/Tests/CodexReleaseTests/DataTests/SwarmTest.cs @@ -1,4 +1,5 @@ -using CodexPlugin; +using CodexClient; +using CodexPlugin; using CodexTests; using FileUtils; using NUnit.Framework; diff --git a/Tests/CodexReleaseTests/DataTests/TwoClientTest.cs b/Tests/CodexReleaseTests/DataTests/TwoClientTest.cs index 676653e7..8ab9c88d 100644 --- a/Tests/CodexReleaseTests/DataTests/TwoClientTest.cs +++ b/Tests/CodexReleaseTests/DataTests/TwoClientTest.cs @@ -1,4 +1,5 @@ -using CodexPlugin; +using CodexClient; +using CodexPlugin; using CodexTests; using NUnit.Framework; using Utils; diff --git a/Tests/CodexReleaseTests/DataTests/UnknownCidTest.cs b/Tests/CodexReleaseTests/DataTests/UnknownCidTest.cs index d0699a53..2d64e863 100644 --- a/Tests/CodexReleaseTests/DataTests/UnknownCidTest.cs +++ b/Tests/CodexReleaseTests/DataTests/UnknownCidTest.cs @@ -1,11 +1,6 @@ -using CodexPlugin; +using CodexClient; using CodexTests; using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace CodexReleaseTests.DataTests { diff --git a/Tests/CodexReleaseTests/MarketTests/ContractFailedTest.cs b/Tests/CodexReleaseTests/MarketTests/ContractFailedTest.cs index dd79c6af..17454e46 100644 --- a/Tests/CodexReleaseTests/MarketTests/ContractFailedTest.cs +++ b/Tests/CodexReleaseTests/MarketTests/ContractFailedTest.cs @@ -1,13 +1,6 @@ -using CodexContractsPlugin; +using CodexClient; using CodexContractsPlugin.Marketplace; -using CodexPlugin; -using CodexTests; using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Utils; namespace CodexReleaseTests.MarketTests diff --git a/Tests/CodexReleaseTests/MarketTests/ContractSuccessfulTest.cs b/Tests/CodexReleaseTests/MarketTests/ContractSuccessfulTest.cs index d614840f..8788f204 100644 --- a/Tests/CodexReleaseTests/MarketTests/ContractSuccessfulTest.cs +++ b/Tests/CodexReleaseTests/MarketTests/ContractSuccessfulTest.cs @@ -1,7 +1,4 @@ using CodexClient; -using CodexContractsPlugin; -using CodexPlugin; -using GethPlugin; using NUnit.Framework; using Utils; @@ -32,7 +29,7 @@ namespace CodexReleaseTests.MarketTests request.WaitForStorageContractStarted(); AssertContractSlotsAreFilledByHosts(request, hosts); - request.WaitForStorageContractFinished(GetContracts()); + request.WaitForStorageContractFinished(); AssertClientHasPaidForContract(pricePerSlotPerSecond, client, request, hosts); AssertHostsWerePaidForContract(pricePerSlotPerSecond, request, hosts); diff --git a/Tests/CodexReleaseTests/MarketTests/MarketplaceAutoBootstrapDistTest.cs b/Tests/CodexReleaseTests/MarketTests/MarketplaceAutoBootstrapDistTest.cs index ef181c33..6a4f6dd2 100644 --- a/Tests/CodexReleaseTests/MarketTests/MarketplaceAutoBootstrapDistTest.cs +++ b/Tests/CodexReleaseTests/MarketTests/MarketplaceAutoBootstrapDistTest.cs @@ -1,4 +1,5 @@ -using CodexContractsPlugin; +using CodexClient; +using CodexContractsPlugin; using CodexContractsPlugin.Marketplace; using CodexPlugin; using CodexTests; @@ -67,7 +68,7 @@ namespace CodexReleaseTests.MarketTests Assert.That(GetTstBalance(host).TstWei, Is.EqualTo(StartingBalanceTST.Tst().TstWei)); Assert.That(GetEthBalance(host).Wei, Is.EqualTo(StartingBalanceEth.Eth().Wei)); - host.Marketplace.MakeStorageAvailable(new CodexPlugin.StorageAvailability( + host.Marketplace.MakeStorageAvailable(new StorageAvailability( totalSpace: HostAvailabilitySize, maxDuration: HostAvailabilityMaxDuration, minPriceForTotalSpace: 1.TstWei(), diff --git a/Tests/CodexReleaseTests/MarketTests/MultipleContractsTest.cs b/Tests/CodexReleaseTests/MarketTests/MultipleContractsTest.cs index 97876f2b..50d1a7c9 100644 --- a/Tests/CodexReleaseTests/MarketTests/MultipleContractsTest.cs +++ b/Tests/CodexReleaseTests/MarketTests/MultipleContractsTest.cs @@ -1,7 +1,4 @@ using CodexClient; -using CodexContractsPlugin; -using CodexPlugin; -using GethPlugin; using NUnit.Framework; using Utils; @@ -36,7 +33,7 @@ namespace CodexReleaseTests.MarketTests All(requests, r => r.WaitForStorageContractStarted()); All(requests, r => AssertContractSlotsAreFilledByHosts(r, hosts)); - All(requests, r => r.WaitForStorageContractFinished(GetContracts())); + All(requests, r => r.WaitForStorageContractFinished()); // todo: removed from codexclient: //contracts.WaitUntilNextPeriod(); diff --git a/Tests/CodexReleaseTests/NodeTests/PeerTableTests.cs b/Tests/CodexReleaseTests/NodeTests/PeerTableTests.cs index ced42922..74715b66 100644 --- a/Tests/CodexReleaseTests/NodeTests/PeerTableTests.cs +++ b/Tests/CodexReleaseTests/NodeTests/PeerTableTests.cs @@ -1,13 +1,7 @@ -using CodexPlugin; +using CodexClient; using CodexTests; using CodexTests.Helpers; using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Utils; namespace CodexReleaseTests.NodeTests { diff --git a/Tests/ExperimentalTests/BasicTests/ExampleTests.cs b/Tests/ExperimentalTests/BasicTests/ExampleTests.cs index fc83da3b..716d3fff 100644 --- a/Tests/ExperimentalTests/BasicTests/ExampleTests.cs +++ b/Tests/ExperimentalTests/BasicTests/ExampleTests.cs @@ -1,4 +1,5 @@ -using CodexPlugin; +using CodexClient; +using CodexPlugin; using DistTestCore; using MetricsPlugin; using NUnit.Framework; diff --git a/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs b/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs index 8ce80571..c62db4a0 100644 --- a/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs +++ b/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs @@ -108,7 +108,7 @@ namespace CodexTests.BasicTests AssertStorageRequest(request, purchase, contracts, client); AssertContractSlot(contracts, request, 0); - purchaseContract.WaitForStorageContractFinished(contracts); + purchaseContract.WaitForStorageContractFinished(); // todo: removed from codexclient: //contracts.WaitUntilNextPeriod(); diff --git a/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs b/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs index 88ee0dbb..b0373a32 100644 --- a/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs +++ b/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs @@ -1,6 +1,7 @@ using CodexContractsPlugin; using CodexClient; using NUnit.Framework; +using Utils; namespace CodexTests.PeerDiscoveryTests { diff --git a/Tests/ExperimentalTests/UtilityTests/DiscordBotTests.cs b/Tests/ExperimentalTests/UtilityTests/DiscordBotTests.cs index 5559c5f6..320c3d86 100644 --- a/Tests/ExperimentalTests/UtilityTests/DiscordBotTests.cs +++ b/Tests/ExperimentalTests/UtilityTests/DiscordBotTests.cs @@ -20,7 +20,7 @@ namespace CodexTests.UtilityTests private readonly RewardRepo repo = new RewardRepo(); private readonly TestToken hostInitialBalance = 3000000.TstWei(); private readonly TestToken clientInitialBalance = 1000000000.TstWei(); - private readonly EthAccount clientAccount = EthAccount.GenerateNew(); + private readonly EthAccount clientAccount = EthAccountGenerator.GenerateNew(); private readonly List hostAccounts = new List(); private readonly List rewardsSeen = new List(); private readonly TimeSpan rewarderInterval = TimeSpan.FromMinutes(1); @@ -46,7 +46,7 @@ namespace CodexTests.UtilityTests var purchaseContract = ClientPurchasesStorage(client); purchaseContract.WaitForStorageContractStarted(); - purchaseContract.WaitForStorageContractFinished(contracts); + purchaseContract.WaitForStorageContractFinished(); // todo: removed from codexclient: //contracts.WaitUntilNextPeriod(); diff --git a/Tools/AutoClient/App.cs b/Tools/AutoClient/App.cs index cf2573a7..0cdfc673 100644 --- a/Tools/AutoClient/App.cs +++ b/Tools/AutoClient/App.cs @@ -1,4 +1,5 @@ using AutoClient.Modes.FolderStore; +using CodexClient; using Logging; namespace AutoClient @@ -15,7 +16,6 @@ namespace AutoClient ); Generator = CreateGenerator(); - CidRepo = new CidRepo(config); Performance = new Performance(new LogSplitter( new FileLog(Path.Combine(config.LogPath, "performance")), new ConsoleLog() @@ -29,15 +29,17 @@ namespace AutoClient { FolderWorkDispatcher = null!; } + + CodexNodeFactory = new CodexNodeFactory(log: Log, dataDir: Config.DataPath); } public Configuration Config { get; } public ILog Log { get; } public IFileGenerator Generator { get; } public CancellationTokenSource Cts { get; } = new CancellationTokenSource(); - public CidRepo CidRepo { get; } public Performance Performance { get; } public FolderWorkDispatcher FolderWorkDispatcher { get; } + public CodexNodeFactory CodexNodeFactory { get; } private IFileGenerator CreateGenerator() { diff --git a/Tools/AutoClient/AutomaticPurchaser.cs b/Tools/AutoClient/AutomaticPurchaser.cs index 1e217167..1886f321 100644 --- a/Tools/AutoClient/AutomaticPurchaser.cs +++ b/Tools/AutoClient/AutomaticPurchaser.cs @@ -1,24 +1,19 @@ -using CodexOpenApi; -using CodexPlugin; -using Logging; -using Newtonsoft.Json; -using Utils; +using Logging; namespace AutoClient { public class AutomaticPurchaser { + private readonly App app; private readonly ILog log; - private readonly ICodexInstance instance; - private readonly CodexNode codex; + private readonly CodexWrapper node; private Task workerTask = Task.CompletedTask; - private App app => instance.App; - public AutomaticPurchaser(ILog log, ICodexInstance instance, CodexNode codex) + public AutomaticPurchaser(App app, ILog log, CodexWrapper node) { + this.app = app; this.log = log; - this.instance = instance; - this.codex = codex; + this.node = node; } public void Start() @@ -40,7 +35,6 @@ namespace AutoClient { var pid = await StartNewPurchase(); await WaitTillFinished(pid); - await DownloadForeignCid(); } catch (Exception ex) { @@ -50,27 +44,13 @@ namespace AutoClient } } - private async Task DownloadForeignCid() - { - var cid = app.CidRepo.GetForeignCid(instance.NodeId); - if (cid == null) return; - - var size = app.CidRepo.GetSizeForCid(cid); - if (size == null) return; - - var filename = Guid.NewGuid().ToString().ToLowerInvariant(); - await codex.DownloadCid(filename, cid, size); - - DeleteFile(filename); - } - private async Task StartNewPurchase() { var file = await CreateFile(); try { - var cid = await codex.UploadFile(file); - var response = await codex.RequestStorage(cid); + var cid = node.UploadFile(file); + var response = node.RequestStorage(cid); return response.PurchaseId; } finally @@ -92,7 +72,7 @@ namespace AutoClient } catch (Exception exc) { - app.Log.Error($"Failed to delete file '{file}': {exc}"); + log.Error($"Failed to delete file '{file}': {exc}"); } } @@ -103,7 +83,7 @@ namespace AutoClient var emptyResponseTolerance = 10; while (!app.Cts.Token.IsCancellationRequested) { - var purchase = await codex.GetStoragePurchase(pid); + var purchase = node.GetStoragePurchase(pid); if (purchase == null) { await FixedShortDelay(); diff --git a/Tools/AutoClient/CidRepo.cs b/Tools/AutoClient/CidRepo.cs deleted file mode 100644 index 572b5ec9..00000000 --- a/Tools/AutoClient/CidRepo.cs +++ /dev/null @@ -1,86 +0,0 @@ -namespace AutoClient -{ - public class CidRepo - { - private readonly Random random = new Random(); - private readonly object _lock = new object(); - private readonly List entries = new List(); - private readonly Configuration config; - - public CidRepo(Configuration config) - { - this.config = config; - } - - public void Add(string nodeId, string cid, long knownSize) - { - lock (_lock) - { - entries.Add(new CidEntry(nodeId, cid, knownSize)); - if (entries.Count > 1000) entries.Clear(); - } - } - - public void AddEncoded(string originalCid, string encodedCid) - { - lock (_lock) - { - var entry = entries.SingleOrDefault(e => e.Cid == originalCid); - if (entry == null) return; - - entry.Encoded = encodedCid; - } - } - - public string? GetForeignCid(string myNodeId) - { - lock (_lock) - { - while (true) - { - if (!entries.Any()) return null; - var available = entries.Where(e => e.NodeId != myNodeId).ToArray(); - if (!available.Any()) return null; - - var i = random.Next(0, available.Length); - var entry = available[i]; - - if (entry.CreatedUtc < (DateTime.UtcNow + TimeSpan.FromMinutes(config.ContractDurationMinutes))) - { - entries.Remove(entry); - } - else - { - return entry.Cid; - } - } - } - } - - public long? GetSizeForCid(string cid) - { - lock (_lock) - { - var entry = entries.SingleOrDefault(e => e.Cid == cid); - if (entry == null) return null; - return entry.KnownSize; - } - } - } - - public class CidEntry - { - public CidEntry(string nodeId, string cid, long knownSize) - { - NodeId = nodeId; - Cid = cid; - KnownSize = knownSize; - } - - public string NodeId { get; } - public string Cid { get; } - public string Encoded { get; set; } = string.Empty; - public long KnownSize { get; } - public DateTime CreatedUtc { get; } = DateTime.UtcNow; - } -} diff --git a/Tools/AutoClient/CodexContext.cs b/Tools/AutoClient/CodexContext.cs new file mode 100644 index 00000000..98fd1c95 --- /dev/null +++ b/Tools/AutoClient/CodexContext.cs @@ -0,0 +1,32 @@ +using CodexClient; +using Utils; + +namespace AutoClient +{ + public interface ICodexContext + { + string NodeId { get; } + App App { get; } + ICodexNode Codex { get; } + HttpClient Client { get; } + Address Address { get; } + } + + public class CodexContext : ICodexContext + { + public CodexContext(App app, ICodexNode codex, HttpClient client, Address address) + { + App = app; + Codex = codex; + Client = client; + Address = address; + NodeId = Guid.NewGuid().ToString(); + } + + public string NodeId { get; } + public App App { get; } + public ICodexNode Codex { get; } + public HttpClient Client { get; } + public Address Address { get; } + } +} diff --git a/Tools/AutoClient/CodexInstance.cs b/Tools/AutoClient/CodexInstance.cs deleted file mode 100644 index 3be59d45..00000000 --- a/Tools/AutoClient/CodexInstance.cs +++ /dev/null @@ -1,162 +0,0 @@ -using CodexClient; -using CodexOpenApi; -using CodexPlugin; -using Logging; -using Nethereum.Model; -using Newtonsoft.Json; -using Utils; - -namespace AutoClient -{ - public interface ICodexInstance - { - string NodeId { get; } - App App { get; } - CodexApi Codex { get; } - HttpClient Client { get; } - Address Address { get; } - } - - public class CodexInstance : ICodexInstance - { - public CodexInstance(App app, CodexApi codex, HttpClient client, Address address) - { - App = app; - Codex = codex; - Client = client; - Address = address; - NodeId = Guid.NewGuid().ToString(); - } - - public string NodeId { get; } - public App App { get; } - public CodexApi Codex { get; } - public HttpClient Client { get; } - public Address Address { get; } - } - - public class CodexNode - { - private readonly App app; - private readonly ICodexInstance codex; - - public CodexNode(App app, ICodexInstance instance) - { - this.app = app; - codex = instance; - } - - public async Task DownloadCid(string filename, string cid, long? size) - { - try - { - var sw = System.Diagnostics.Stopwatch.StartNew(); - using var fileStream = File.OpenWrite(filename); - var fileResponse = await codex.Codex.DownloadNetworkStreamAsync(cid); - fileResponse.Stream.CopyTo(fileStream); - var time = sw.Elapsed; - app.Performance.DownloadSuccessful(size, time); - } - catch (Exception ex) - { - app.Performance.DownloadFailed(ex); - } - } - - public async Task UploadFile(string filename) - { - using var fileStream = File.OpenRead(filename); - try - { - var info = new FileInfo(filename); - var sw = System.Diagnostics.Stopwatch.StartNew(); - var cid = await UploadStream(fileStream, filename); - var time = sw.Elapsed; - app.Performance.UploadSuccessful(info.Length, time); - app.CidRepo.Add(codex.NodeId, cid.Id, info.Length); - return cid; - } - catch (Exception exc) - { - app.Performance.UploadFailed(exc); - throw; - } - } - - public async Task RequestStorage(ContentId cid) - { - app.Log.Debug("Requesting storage for " + cid.Id); - var result = await codex.Codex.CreateStorageRequestAsync(cid.Id, new StorageRequestCreation() - { - Collateral = app.Config.RequiredCollateral.ToString(), - Duration = (app.Config.ContractDurationMinutes * 60).ToString(), - Expiry = (app.Config.ContractExpiryMinutes * 60).ToString(), - Nodes = app.Config.NumHosts, - Reward = app.Config.Price.ToString(), - ProofProbability = "15", - Tolerance = app.Config.HostTolerance - }, app.Cts.Token); - - app.Log.Debug("Purchase ID: " + result); - - var encoded = await GetEncodedCid(result); - app.CidRepo.AddEncoded(cid.Id, encoded); - - return new RequestStorageResult(result, new ContentId(encoded)); - } - - public class RequestStorageResult - { - public RequestStorageResult(string purchaseId, ContentId encodedCid) - { - PurchaseId = purchaseId; - EncodedCid = encodedCid; - } - - public string PurchaseId { get; } - public ContentId EncodedCid { get; } - - public override string ToString() - { - return $"{PurchaseId} (cid: {EncodedCid})"; - } - } - - public async Task GetStoragePurchase(string pid) - { - // openapi still don't match code. - var str = await codex.Client.GetStringAsync($"{codex.Address.Host}:{codex.Address.Port}/api/codex/v1/storage/purchases/{pid}"); - if (string.IsNullOrEmpty(str)) return null; - return JsonConvert.DeserializeObject(str); - } - - private async Task UploadStream(FileStream fileStream, string filename) - { - app.Log.Debug($"Uploading file..."); - var response = await codex.Codex.UploadAsync( - content_type: "application/octet-stream", - content_disposition: $"attachment; filename=\"{filename}\"", - fileStream, app.Cts.Token); - - if (string.IsNullOrEmpty(response)) FrameworkAssert.Fail("Received empty response."); - if (response.StartsWith("Unable to store block")) FrameworkAssert.Fail("Node failed to store block."); - - app.Log.Debug($"Uploaded file. Received contentId: '{response}'."); - return new ContentId(response); - } - - private async Task GetEncodedCid(string pid) - { - try - { - var sp = (await GetStoragePurchase(pid))!; - return sp.Request.Content.Cid; - } - catch (Exception ex) - { - app.Log.Error(ex.ToString()); - throw; - } - } - } -} diff --git a/Tools/AutoClient/CodexWrapper.cs b/Tools/AutoClient/CodexWrapper.cs new file mode 100644 index 00000000..2369f2f8 --- /dev/null +++ b/Tools/AutoClient/CodexWrapper.cs @@ -0,0 +1,45 @@ +using CodexClient; +using FileUtils; +using Utils; + +namespace AutoClient +{ + public class CodexWrapper + { + private readonly App app; + + public CodexWrapper(App app, ICodexNode node) + { + this.app = app; + Node = node; + } + + public ICodexNode Node { get; } + + public ContentId UploadFile(string filepath) + { + return Node.UploadFile(TrackedFile.FromPath(app.Log, filepath)); + } + + public IStoragePurchaseContract RequestStorage(ContentId cid) + { + app.Log.Debug("Requesting storage for " + cid.Id); + var result = Node.Marketplace.RequestStorage(new StoragePurchaseRequest(cid) + { + RequiredCollateral = app.Config.RequiredCollateral.Tst(), + Duration = TimeSpan.FromMinutes(app.Config.ContractDurationMinutes), + Expiry = TimeSpan.FromMinutes(app.Config.ContractExpiryMinutes), + MinRequiredNumberOfNodes = Convert.ToUInt32(app.Config.NumHosts), + NodeFailureTolerance = Convert.ToUInt32(app.Config.HostTolerance), + PricePerSlotPerSecond = app.Config.Price.Tst(), + ProofProbability = 15 + }); + return result; + } + + public StoragePurchase? GetStoragePurchase(string pid) + { + return Node.GetPurchaseStatus(pid); + } + } +} diff --git a/Tools/AutoClient/Modes/FolderStore/FileWorker.cs b/Tools/AutoClient/Modes/FolderStore/FileWorker.cs index 5953eff3..0459815c 100644 --- a/Tools/AutoClient/Modes/FolderStore/FileWorker.cs +++ b/Tools/AutoClient/Modes/FolderStore/FileWorker.cs @@ -1,30 +1,29 @@ -using Logging; +using CodexClient; +using Logging; namespace AutoClient.Modes.FolderStore { public class FileWorker : FileStatus { private readonly App app; + private readonly CodexWrapper node; private readonly ILog log; - private readonly ICodexInstance instance; private readonly PurchaseInfo purchaseInfo; private readonly string sourceFilename; private readonly Action onFileUploaded; private readonly Action onNewPurchase; - private readonly CodexNode codex; - public FileWorker(App app, ICodexInstance instance, PurchaseInfo purchaseInfo, string folder, FileIndex fileIndex, Action onFileUploaded, Action onNewPurchase) + public FileWorker(App app, CodexWrapper node, PurchaseInfo purchaseInfo, string folder, FileIndex fileIndex, Action onFileUploaded, Action onNewPurchase) : base(app, folder, fileIndex.File + ".json", purchaseInfo) { this.app = app; + this.node = node; log = new LogPrefixer(app.Log, GetFileTag(fileIndex)); - this.instance = instance; this.purchaseInfo = purchaseInfo; sourceFilename = fileIndex.File; if (sourceFilename.ToLowerInvariant().EndsWith(".json")) throw new Exception("Not an era file."); this.onFileUploaded = onFileUploaded; this.onNewPurchase = onNewPurchase; - codex = new CodexNode(app, instance); } public int FailureCounter => State.FailureCounter; @@ -34,14 +33,14 @@ namespace AutoClient.Modes.FolderStore newState.LastUpdate = DateTime.MinValue; } - public async Task Update() + public void Update() { try { if (IsCurrentlyRunning() && UpdatedRecently()) return; Log($"Updating for '{sourceFilename}'..."); - await EnsureRecentPurchase(); + EnsureRecentPurchase(); SaveState(); app.Log.Log(""); } @@ -60,12 +59,12 @@ namespace AutoClient.Modes.FolderStore return State.LastUpdate + TimeSpan.FromMinutes(15) > now; } - private async Task EnsureCid() + private string EnsureCid() { Log($"Checking CID..."); if (!string.IsNullOrEmpty(State.EncodedCid) && - await DoesCidExistInNetwork(State.EncodedCid)) + DoesCidExistInNetwork(State.EncodedCid)) { Log("Encoded-CID successfully found in the network."); // TODO: Using the encoded CID currently would result in double-encoding of the dataset. @@ -75,7 +74,7 @@ namespace AutoClient.Modes.FolderStore } if (!string.IsNullOrEmpty(State.Cid) && - await DoesCidExistInNetwork(State.Cid)) + DoesCidExistInNetwork(State.Cid)) { Log("Basic-CID successfully found in the network."); return State.Cid; @@ -87,7 +86,7 @@ namespace AutoClient.Modes.FolderStore } Log($"Uploading..."); - var cid = await codex.UploadFile(sourceFilename); + var cid = node.UploadFile(sourceFilename); onFileUploaded(); Log("Got Basic-CID: " + cid); State.Cid = cid.Id; @@ -95,7 +94,7 @@ namespace AutoClient.Modes.FolderStore return State.Cid; } - private async Task DoesCidExistInNetwork(string cid) + private bool DoesCidExistInNetwork(string cid) { try { @@ -107,7 +106,7 @@ namespace AutoClient.Modes.FolderStore cts.Cancel(); }); - var manifest = await instance.Codex.DownloadNetworkManifestAsync(cid, cts.Token); + var manifest = node.Node.DownloadManifestOnly(new ContentId(cid)); if (manifest == null) return false; } catch @@ -117,23 +116,23 @@ namespace AutoClient.Modes.FolderStore return true; } - private async Task EnsureRecentPurchase() + private void EnsureRecentPurchase() { Log($"Checking recent purchase..."); var recent = GetMostRecent(); if (recent == null) { Log($"No recent purchase."); - await MakeNewPurchase(); + MakeNewPurchase(); return; } - await UpdatePurchase(recent); + UpdatePurchase(recent); if (recent.Expiry.HasValue) { Log($"Purchase has failed or expired."); - await MakeNewPurchase(); + MakeNewPurchase(); State.FailureCounter++; return; } @@ -141,7 +140,7 @@ namespace AutoClient.Modes.FolderStore if (recent.Finish.HasValue) { Log($"Purchase has finished."); - await MakeNewPurchase(); + MakeNewPurchase(); return; } @@ -149,7 +148,7 @@ namespace AutoClient.Modes.FolderStore if (recent.Started.HasValue && DateTime.UtcNow > safeEnd) { Log($"Purchase is going to expire soon."); - await MakeNewPurchase(); + MakeNewPurchase(); return; } @@ -168,12 +167,12 @@ namespace AutoClient.Modes.FolderStore Log($"Purchase is running."); } - private async Task UpdatePurchase(WorkerPurchase recent) + private void UpdatePurchase(WorkerPurchase recent) { if (string.IsNullOrEmpty(recent.Pid)) throw new Exception("No purchaseID!"); var now = DateTime.UtcNow; - var purchase = await codex.GetStoragePurchase(recent.Pid); + var purchase = node.GetStoragePurchase(recent.Pid); if (purchase == null) { Log($"No purchase information found for PID '{recent.Pid}'. Consider this one expired."); @@ -210,15 +209,15 @@ namespace AutoClient.Modes.FolderStore SaveState(); } - private async Task MakeNewPurchase() + private void MakeNewPurchase() { - var cid = await EnsureCid(); + var cid = EnsureCid(); if (string.IsNullOrEmpty(cid)) throw new Exception("No cid!"); Log($"Creating new purchase..."); - var response = await codex.RequestStorage(new CodexPlugin.ContentId(cid)); + var response = node.RequestStorage(new ContentId(cid)); var purchaseId = response.PurchaseId; - var encodedCid = response.EncodedCid; + var encodedCid = response.ContentId; if (string.IsNullOrEmpty(purchaseId) || purchaseId == "Unable to encode manifest" || purchaseId == "Purchasing not available" || @@ -248,7 +247,7 @@ namespace AutoClient.Modes.FolderStore while (DateTime.UtcNow < timeout) { Thread.Sleep(5000); - await UpdatePurchase(newPurchase); + UpdatePurchase(newPurchase); if (newPurchase.Submitted.HasValue) { Log("New purchase successfully submitted."); diff --git a/Tools/AutoClient/Modes/FolderStore/FolderWorkOverview.cs b/Tools/AutoClient/Modes/FolderStore/FolderWorkOverview.cs index 4a5df0eb..aadfe037 100644 --- a/Tools/AutoClient/Modes/FolderStore/FolderWorkOverview.cs +++ b/Tools/AutoClient/Modes/FolderStore/FolderWorkOverview.cs @@ -1,5 +1,4 @@ -using CodexOpenApi; -using System.IO.Compression; +using System.IO.Compression; using static AutoClient.Modes.FolderStore.FolderWorkOverview; namespace AutoClient.Modes.FolderStore @@ -22,7 +21,7 @@ namespace AutoClient.Modes.FolderStore newState.LastOverviewUpdate = DateTime.MinValue; } - public async Task Update(ICodexInstance instance) + public void Update(CodexWrapper instance) { var jsonFiles = Directory.GetFiles(Folder).Where(f => f.ToLowerInvariant().EndsWith(".json") && !f.Contains(OverviewFilename)).ToList(); @@ -54,7 +53,7 @@ namespace AutoClient.Modes.FolderStore State.UncommitedChanges = 0; SaveState(); - await CreateNewOverviewZip(jsonFiles, FilePath, instance); + CreateNewOverviewZip(jsonFiles, FilePath, instance); } } @@ -64,7 +63,7 @@ namespace AutoClient.Modes.FolderStore SaveState(); } - private async Task CreateNewOverviewZip(List jsonFiles, string filePath, ICodexInstance instance) + private void CreateNewOverviewZip(List jsonFiles, string filePath, CodexWrapper node) { Log(""); Log(""); @@ -74,15 +73,14 @@ namespace AutoClient.Modes.FolderStore Log("Uploading to Codex..."); try { - var codex = new CodexNode(app, instance); - var cid = await codex.UploadFile(zipFilename); + var cid = node.UploadFile(zipFilename); Log($"Upload successful: New overview zipfile CID = '{cid.Id}'"); Log("Requesting storage for it..."); - var result = await codex.RequestStorage(cid); - Log("Storage requested. Purchase ID: " + result); + var result = node.RequestStorage(cid); + Log("Storage requested. Purchase ID: " + result.PurchaseId); var outFile = Path.Combine(app.Config.DataPath, "OverviewZip.cid"); - File.AppendAllLines(outFile, [DateTime.UtcNow.ToString("o") + " - " + result.EncodedCid.Id]); + File.AppendAllLines(outFile, [DateTime.UtcNow.ToString("o") + " - " + result.ContentId.Id]); Log($">>> [{outFile}] has been updated. <<<"); } catch (Exception exc) diff --git a/Tools/AutoClient/Modes/FolderStoreMode.cs b/Tools/AutoClient/Modes/FolderStoreMode.cs index 36d73d1d..f0318a64 100644 --- a/Tools/AutoClient/Modes/FolderStoreMode.cs +++ b/Tools/AutoClient/Modes/FolderStoreMode.cs @@ -17,13 +17,13 @@ namespace AutoClient.Modes this.purchaseInfo = purchaseInfo; } - public void Start(ICodexInstance instance, int index) + public void Start(CodexWrapper instance, int index) { - checkTask = Task.Run(async () => + checkTask = Task.Run(() => { try { - await RunChecker(instance); + RunChecker(instance); } catch (Exception ex) { @@ -33,14 +33,14 @@ namespace AutoClient.Modes }); } - private async Task RunChecker(ICodexInstance instance) + private void RunChecker(CodexWrapper instance) { var i = 0; while (!cts.IsCancellationRequested) { Thread.Sleep(2000); - var worker = await ProcessWorkItem(instance); + var worker = ProcessWorkItem(instance); if (worker.FailureCounter > 5) { throw new Exception("Worker has failure count > 5. Stopping AutoClient..."); @@ -51,16 +51,16 @@ namespace AutoClient.Modes { i = 0; var overview = new FolderWorkOverview(app, purchaseInfo, folder); - await overview.Update(instance); + overview.Update(instance); } } } - private async Task ProcessWorkItem(ICodexInstance instance) + private FileWorker ProcessWorkItem(CodexWrapper instance) { var file = app.FolderWorkDispatcher.GetFileToCheck(); var worker = new FileWorker(app, instance, purchaseInfo, folder, file, OnFileUploaded, OnNewPurchase); - await worker.Update(); + worker.Update(); if (worker.IsBusy()) app.FolderWorkDispatcher.WorkerIsBusy(); return worker; } diff --git a/Tools/AutoClient/Modes/Mode.cs b/Tools/AutoClient/Modes/Mode.cs index 5ab33a10..afc76f7a 100644 --- a/Tools/AutoClient/Modes/Mode.cs +++ b/Tools/AutoClient/Modes/Mode.cs @@ -1,14 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace AutoClient.Modes +namespace AutoClient.Modes { public interface IMode { - void Start(ICodexInstance instance, int index); + void Start(CodexWrapper node, int index); void Stop(); } } diff --git a/Tools/AutoClient/Modes/PurchasingMode.cs b/Tools/AutoClient/Modes/PurchasingMode.cs index 52e82215..206ab365 100644 --- a/Tools/AutoClient/Modes/PurchasingMode.cs +++ b/Tools/AutoClient/Modes/PurchasingMode.cs @@ -13,11 +13,11 @@ namespace AutoClient.Modes this.app = app; } - public void Start(ICodexInstance instance, int index) + public void Start(CodexWrapper node, int index) { for (var i = 0; i < app.Config.NumConcurrentPurchases; i++) { - purchasers.Add(new AutomaticPurchaser(new LogPrefixer(app.Log, $"({i}) "), instance, new CodexNode(app, instance))); + purchasers.Add(new AutomaticPurchaser(app, new LogPrefixer(app.Log, $"({i}) "), node)); } var delayPerPurchaser = diff --git a/Tools/AutoClient/Program.cs b/Tools/AutoClient/Program.cs index 8c8aed13..72967d90 100644 --- a/Tools/AutoClient/Program.cs +++ b/Tools/AutoClient/Program.cs @@ -2,7 +2,7 @@ using AutoClient; using AutoClient.Modes; using AutoClient.Modes.FolderStore; -using CodexOpenApi; +using CodexClient; using Utils; public class Program @@ -34,10 +34,11 @@ public class Program public async Task Run() { - var codexInstances = await CreateCodexInstances(); + await Task.CompletedTask; + var codexNodes = CreateCodexWrappers(); var i = 0; - foreach (var cdx in codexInstances) + foreach (var cdx in codexNodes) { var mode = CreateMode(); modes.Add(mode); @@ -74,20 +75,20 @@ public class Program )); } - private async Task CreateCodexInstances() + private CodexWrapper[] CreateCodexWrappers() { var endpointStrs = app.Config.CodexEndpoints.Split(";", StringSplitOptions.RemoveEmptyEntries); - var result = new List(); + var result = new List(); foreach (var e in endpointStrs) { - result.Add(await CreateCodexInstance(e)); + result.Add(CreateCodexWrapper(e)); } return result.ToArray(); } - private async Task CreateCodexInstance(string endpoint) + private CodexWrapper CreateCodexWrapper(string endpoint) { var splitIndex = endpoint.LastIndexOf(':'); var host = endpoint.Substring(0, splitIndex); @@ -99,35 +100,9 @@ public class Program port: port ); - var client = new HttpClient(); - client.Timeout = TimeSpan.FromMinutes(60.0); - var codex = new CodexApi(client); - codex.BaseUrl = $"{address.Host}:{address.Port}/api/codex/v1"; - - app.Log.Log($"Checking Codex at {address}..."); - await CheckCodex(codex); - app.Log.Log("OK"); - - return new CodexInstance( - app, - codex, - client, - address - ); - } - - private async Task CheckCodex(CodexApi codex) - { - try - { - var info = await codex.GetDebugInfoAsync(); - if (string.IsNullOrEmpty(info.Id)) throw new Exception("Failed to fetch Codex node id"); - } - catch (Exception ex) - { - app.Log.Error($"Codex not OK: {ex}"); - throw; - } + var instance = CodexInstance.CreateFromApiEndpoint("ac", address); + var node = app.CodexNodeFactory.CreateCodexNode(instance); + return new CodexWrapper(app, node); } private static void PrintHelp() diff --git a/Tools/BiblioTech/CodexCidChecker.cs b/Tools/BiblioTech/CodexCidChecker.cs index 3b4b8bb9..15728d0d 100644 --- a/Tools/BiblioTech/CodexCidChecker.cs +++ b/Tools/BiblioTech/CodexCidChecker.cs @@ -1,5 +1,4 @@ -using CodexOpenApi; -using IdentityModel.Client; +using CodexClient; using Logging; using Utils; @@ -11,15 +10,26 @@ namespace BiblioTech private readonly Configuration config; private readonly ILog log; private readonly Mutex checkMutex = new Mutex(); - private CodexApi? currentCodexNode; + private readonly CodexNodeFactory factory; + private ICodexNode? currentCodexNode; public CodexCidChecker(Configuration config, ILog log) { this.config = config; this.log = log; + + factory = new CodexNodeFactory(log, dataDir: config.DataPath); + + if (!string.IsNullOrEmpty(config.CodexEndpointAuth) && config.CodexEndpointAuth.Contains(":")) + { + throw new Exception("Todo: codexnodefactory httpfactory support basicauth!"); + //var tokens = config.CodexEndpointAuth.Split(':'); + //if (tokens.Length != 2) throw new Exception("Expected ':' in CodexEndpointAuth parameter."); + //client.SetBasicAuthentication(tokens[0], tokens[1]); + } } - public async Task PerformCheck(string cid) + public CheckResponse PerformCheck(string cid) { if (string.IsNullOrEmpty(config.CodexEndpoint)) { @@ -30,10 +40,10 @@ namespace BiblioTech { checkMutex.WaitOne(); var codex = GetCodex(); - var nodeCheck = await CheckCodex(codex); + var nodeCheck = CheckCodex(codex); if (!nodeCheck) return new CheckResponse(false, "Codex node is not available. Cannot perform check.", $"Codex node at '{config.CodexEndpoint}' did not respond correctly to debug/info."); - return await PerformCheck(codex, cid); + return PerformCheck(codex, cid); } catch (Exception ex) { @@ -45,19 +55,13 @@ namespace BiblioTech } } - private async Task PerformCheck(CodexApi codex, string cid) + private CheckResponse PerformCheck(ICodexNode codex, string cid) { try { - var manifest = await codex.DownloadNetworkManifestAsync(cid); + var manifest = codex.DownloadManifestOnly(new ContentId(cid)); return SuccessMessage(manifest); } - catch (ApiException apiEx) - { - if (apiEx.StatusCode == 400) return CidFormatInvalid(apiEx.Response); - if (apiEx.StatusCode == 404) return FailedToFetch(apiEx.Response); - return UnexpectedReturnCode(apiEx.Response); - } catch (Exception ex) { return UnexpectedException(ex); @@ -66,13 +70,13 @@ namespace BiblioTech #region Response formatting - private CheckResponse SuccessMessage(DataItem content) + private CheckResponse SuccessMessage(LocalDataset content) { return FormatResponse( success: true, title: $"Success: '{content.Cid}'", error: "", - $"size: {content.Manifest.DatasetSize} bytes", + $"size: {content.Manifest.OriginalBytes} bytes", $"blockSize: {content.Manifest.BlockSize} bytes", $"protected: {content.Manifest.Protected}" ); @@ -143,17 +147,17 @@ namespace BiblioTech #region Codex Node API - private CodexApi GetCodex() + private ICodexNode GetCodex() { if (currentCodexNode == null) currentCodexNode = CreateCodex(); return currentCodexNode; } - private async Task CheckCodex(CodexApi codex) + private bool CheckCodex(ICodexNode node) { try { - var info = await currentCodexNode!.GetDebugInfoAsync(); + var info = node.GetDebugInfo(); if (info == null || string.IsNullOrEmpty(info.Id)) return false; return true; } @@ -164,7 +168,7 @@ namespace BiblioTech } } - private CodexApi CreateCodex() + private ICodexNode CreateCodex() { var endpoint = config.CodexEndpoint; var splitIndex = endpoint.LastIndexOf(':'); @@ -177,17 +181,8 @@ namespace BiblioTech port: port ); - var client = new HttpClient(); - if (!string.IsNullOrEmpty(config.CodexEndpointAuth) && config.CodexEndpointAuth.Contains(":")) - { - var tokens = config.CodexEndpointAuth.Split(':'); - if (tokens.Length != 2) throw new Exception("Expected ':' in CodexEndpointAuth parameter."); - client.SetBasicAuthentication(tokens[0], tokens[1]); - } - - var codex = new CodexApi(client); - codex.BaseUrl = $"{address.Host}:{address.Port}/api/codex/v1"; - return codex; + var instance = CodexInstance.CreateFromApiEndpoint("ac", address); + return factory.CreateCodexNode(instance); } #endregion diff --git a/Tools/BiblioTech/Commands/CheckCidCommand.cs b/Tools/BiblioTech/Commands/CheckCidCommand.cs index a2314280..1e77ce26 100644 --- a/Tools/BiblioTech/Commands/CheckCidCommand.cs +++ b/Tools/BiblioTech/Commands/CheckCidCommand.cs @@ -1,10 +1,5 @@ using BiblioTech.Options; -using System.IO; -using System.Linq; -using System.Threading.Tasks; using Discord; -using BiblioTech.Rewards; -using System.Data; namespace BiblioTech.Commands { @@ -38,7 +33,7 @@ namespace BiblioTech.Commands return; } - var response = await checker.PerformCheck(cid); + var response = checker.PerformCheck(cid); await Program.AdminChecker.SendInAdminChannel($"User {Mention(user)} used '/{Name}' for cid '{cid}'. Lookup-success: {response.Success}. Message: '{response.Message}' Error: '{response.Error}'"); if (response.Success) diff --git a/Tools/BiblioTech/Commands/GetBalanceCommand.cs b/Tools/BiblioTech/Commands/GetBalanceCommand.cs index 8d2460d6..abd19400 100644 --- a/Tools/BiblioTech/Commands/GetBalanceCommand.cs +++ b/Tools/BiblioTech/Commands/GetBalanceCommand.cs @@ -1,6 +1,7 @@ using BiblioTech.Options; using CodexContractsPlugin; using GethPlugin; +using Utils; namespace BiblioTech.Commands { diff --git a/Tools/BiblioTech/Rewards/RoleDriver.cs b/Tools/BiblioTech/Rewards/RoleDriver.cs index fd5cde8e..1964da8e 100644 --- a/Tools/BiblioTech/Rewards/RoleDriver.cs +++ b/Tools/BiblioTech/Rewards/RoleDriver.cs @@ -3,6 +3,7 @@ using Discord.WebSocket; using DiscordRewards; using Logging; using Newtonsoft.Json; +using Utils; namespace BiblioTech.Rewards { @@ -138,7 +139,7 @@ namespace BiblioTech.Rewards { try { - var userData = Program.UserRepo.GetUserDataForAddress(new GethPlugin.EthAddress(address)); + var userData = Program.UserRepo.GetUserDataForAddress(new EthAddress(address)); if (userData != null) log.Log($"User '{userData.Name}' was looked up."); else log.Log($"Lookup for user was unsuccessful. EthAddress: '{address}'"); return userData; diff --git a/Tools/CodexNetDeployer/CodexNodeStarter.cs b/Tools/CodexNetDeployer/CodexNodeStarter.cs index d970b016..fc5c9e81 100644 --- a/Tools/CodexNetDeployer/CodexNodeStarter.cs +++ b/Tools/CodexNetDeployer/CodexNodeStarter.cs @@ -1,4 +1,5 @@ -using CodexContractsPlugin; +using CodexClient; +using CodexContractsPlugin; using CodexPlugin; using Core; using GethPlugin; diff --git a/Tools/CodexNetDeployer/Configuration.cs b/Tools/CodexNetDeployer/Configuration.cs index 441e355a..91134837 100644 --- a/Tools/CodexNetDeployer/Configuration.cs +++ b/Tools/CodexNetDeployer/Configuration.cs @@ -1,5 +1,5 @@ using ArgsUniform; -using CodexPlugin; +using CodexClient; using DistTestCore; namespace CodexNetDeployer diff --git a/Tools/CodexNetDeployer/Deployer.cs b/Tools/CodexNetDeployer/Deployer.cs index 1981b81a..1eb97954 100644 --- a/Tools/CodexNetDeployer/Deployer.cs +++ b/Tools/CodexNetDeployer/Deployer.cs @@ -1,4 +1,5 @@ using BlockchainUtils; +using CodexClient; using CodexContractsPlugin; using CodexDiscordBotPlugin; using CodexPlugin; @@ -7,6 +8,7 @@ using GethPlugin; using KubernetesWorkflow.Types; using Logging; using MetricsPlugin; +using WebUtils; namespace CodexNetDeployer { @@ -100,7 +102,7 @@ namespace CodexNetDeployer retryDelay: TimeSpan.FromSeconds(10), kubernetesNamespace: config.KubeNamespace); - var result = new EntryPoint(log, configuration, string.Empty, new FastHttpTimeSet()); + var result = new EntryPoint(log, configuration, string.Empty, new FastHttpTimeSet(), new DefaultK8sTimeSet()); configuration.Hooks = new K8sHook(config.TestsTypePodLabel, config.DeployId, result.GetPluginMetadata()); return result; @@ -248,7 +250,7 @@ namespace CodexNetDeployer } } - public class FastHttpTimeSet : ITimeSet + public class FastHttpTimeSet : IWebCallTimeSet { public TimeSpan HttpCallRetryDelay() { @@ -264,15 +266,5 @@ namespace CodexNetDeployer { return TimeSpan.FromSeconds(10); } - - public TimeSpan K8sOperationTimeout() - { - return TimeSpan.FromMinutes(10); - } - - public TimeSpan K8sOperationRetryDelay() - { - return TimeSpan.FromSeconds(30); - } } } \ No newline at end of file From 064fc241cb8c207f8dbccc44d95a417319c80b87 Mon Sep 17 00:00:00 2001 From: ThatBen Date: Thu, 16 Jan 2025 15:16:00 +0100 Subject: [PATCH 12/25] classname for generated openapi client --- ProjectPlugins/CodexClient/CodexAccess.cs | 11 ++++++----- ProjectPlugins/CodexClient/CodexClient.csproj | 6 +----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/ProjectPlugins/CodexClient/CodexAccess.cs b/ProjectPlugins/CodexClient/CodexAccess.cs index c7c3dc32..7f35d233 100644 --- a/ProjectPlugins/CodexClient/CodexAccess.cs +++ b/ProjectPlugins/CodexClient/CodexAccess.cs @@ -1,4 +1,5 @@ -using Logging; +using CodexOpenApi; +using Logging; using Newtonsoft.Json; using Utils; using WebUtils; @@ -218,22 +219,22 @@ namespace CodexClient processControl.DeleteDataDirFolder(); } - private T OnCodex(Func> action) + private T OnCodex(Func> action) { var result = httpFactory.CreateHttp(GetHttpId(), h => CheckContainerCrashed()).OnClient(client => CallCodex(client, action)); return result; } - private T OnCodex(Func> action, Retry retry) + private T OnCodex(Func> action, Retry retry) { var result = httpFactory.CreateHttp(GetHttpId(), h => CheckContainerCrashed()).OnClient(client => CallCodex(client, action), retry); return result; } - private T CallCodex(HttpClient client, Func> action) + private T CallCodex(HttpClient client, Func> action) { var address = GetAddress(); - var api = new openapiClient(client); + var api = new CodexApiClient(client); api.BaseUrl = $"{address.Host}:{address.Port}/api/codex/v1"; return CrashCheck(() => Time.Wait(action(api))); } diff --git a/ProjectPlugins/CodexClient/CodexClient.csproj b/ProjectPlugins/CodexClient/CodexClient.csproj index dc11b863..cc417a9e 100644 --- a/ProjectPlugins/CodexClient/CodexClient.csproj +++ b/ProjectPlugins/CodexClient/CodexClient.csproj @@ -11,11 +11,7 @@ - - NSwagCSharp - CodexOpenApi - - + From 2586f4a7561b6ea61f3d6abc5932402b1f808c37 Mon Sep 17 00:00:00 2001 From: ThatBen Date: Sat, 25 Jan 2025 14:11:50 +0100 Subject: [PATCH 13/25] update --- Tools/AutoClient/CodexWrapper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tools/AutoClient/CodexWrapper.cs b/Tools/AutoClient/CodexWrapper.cs index 2369f2f8..30567572 100644 --- a/Tools/AutoClient/CodexWrapper.cs +++ b/Tools/AutoClient/CodexWrapper.cs @@ -26,12 +26,12 @@ namespace AutoClient app.Log.Debug("Requesting storage for " + cid.Id); var result = Node.Marketplace.RequestStorage(new StoragePurchaseRequest(cid) { - RequiredCollateral = app.Config.RequiredCollateral.Tst(), + CollateralPerByte = app.Config.CollateralPerByte.Tst(), Duration = TimeSpan.FromMinutes(app.Config.ContractDurationMinutes), Expiry = TimeSpan.FromMinutes(app.Config.ContractExpiryMinutes), MinRequiredNumberOfNodes = Convert.ToUInt32(app.Config.NumHosts), NodeFailureTolerance = Convert.ToUInt32(app.Config.HostTolerance), - PricePerSlotPerSecond = app.Config.Price.Tst(), + PricePerBytePerSecond = app.Config.PricePerBytePerSecond.Tst(), ProofProbability = 15 }); return result; From 940b8122e7ae6162e294cfd8536855b8dc21c50a Mon Sep 17 00:00:00 2001 From: ThatBen Date: Mon, 27 Jan 2025 09:49:21 +0100 Subject: [PATCH 14/25] Replaces Pod types on codex plugin and starter interface --- ProjectPlugins/CodexPlugin/CodexPlugin.cs | 9 +++-- ProjectPlugins/CodexPlugin/CodexStarter.cs | 36 ++++++++++--------- .../CodexPlugin/CoreInterfaceExtensions.cs | 7 ++-- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/ProjectPlugins/CodexPlugin/CodexPlugin.cs b/ProjectPlugins/CodexPlugin/CodexPlugin.cs index d47da7c7..57e68d09 100644 --- a/ProjectPlugins/CodexPlugin/CodexPlugin.cs +++ b/ProjectPlugins/CodexPlugin/CodexPlugin.cs @@ -1,7 +1,6 @@ using CodexClient; using CodexClient.Hooks; using Core; -using KubernetesWorkflow.Types; namespace CodexPlugin { @@ -34,16 +33,16 @@ namespace CodexPlugin { } - public RunningPod[] DeployCodexNodes(int numberOfNodes, Action setup) + public ICodexInstance[] DeployCodexNodes(int numberOfNodes, Action setup) { var codexSetup = GetSetup(numberOfNodes, setup); return codexStarter.BringOnline(codexSetup); } - public ICodexNodeGroup WrapCodexContainers(CoreInterface coreInterface, RunningPod[] containers) + public ICodexNodeGroup WrapCodexContainers(CoreInterface coreInterface, ICodexInstance[] instances) { - containers = containers.Select(c => SerializeGate.Gate(c)).ToArray(); - return codexStarter.WrapCodexContainers(coreInterface, containers); + instances = instances.Select(c => SerializeGate.Gate(c as CodexInstance)).ToArray(); + return codexStarter.WrapCodexContainers(coreInterface, instances); } public void WireUpMarketplace(ICodexNodeGroup result, Action setup) diff --git a/ProjectPlugins/CodexPlugin/CodexStarter.cs b/ProjectPlugins/CodexPlugin/CodexStarter.cs index 1e74a13e..08c31efd 100644 --- a/ProjectPlugins/CodexPlugin/CodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/CodexStarter.cs @@ -13,7 +13,7 @@ namespace CodexPlugin private readonly IPluginTools pluginTools; private readonly CodexContainerRecipe recipe = new CodexContainerRecipe(); private readonly ApiChecker apiChecker; - private readonly Dictionary podMap = new Dictionary(); + private readonly Dictionary processControlMap = new Dictionary(); private DebugInfoVersion? versionResponse; public CodexStarter(IPluginTools pluginTools) @@ -27,14 +27,10 @@ namespace CodexPlugin public IProcessControl CreateProcessControl(ICodexInstance instance) { - var pod = podMap[instance]; - return new CodexContainerProcessControl(pluginTools, pod, onStop: () => - { - podMap.Remove(instance); - }); + return processControlMap[instance.Name]; } - public RunningPod[] BringOnline(CodexSetup codexSetup) + public ICodexInstance[] BringOnline(CodexSetup codexSetup) { LogSeparator(); Log($"Starting {codexSetup.Describe()}..."); @@ -54,10 +50,10 @@ namespace CodexPlugin } LogSeparator(); - return containers; + return containers.Select(CreateInstance).ToArray(); } - public ICodexNodeGroup WrapCodexContainers(CoreInterface coreInterface, RunningPod[] containers) + public ICodexNodeGroup WrapCodexContainers(CoreInterface coreInterface, ICodexInstance[] instances) { var codexNodeFactory = new CodexNodeFactory( log: pluginTools.GetLog(), @@ -66,7 +62,7 @@ namespace CodexPlugin httpFactory: pluginTools, processControlFactory: this); - var group = CreateCodexGroup(coreInterface, containers, codexNodeFactory); + var group = CreateCodexGroup(coreInterface, instances, codexNodeFactory); Log($"Codex version: {group.Version}"); versionResponse = group.Version; @@ -114,9 +110,8 @@ namespace CodexPlugin return workflow.GetPodInfo(rc); } - private CodexNodeGroup CreateCodexGroup(CoreInterface coreInterface, RunningPod[] runningContainers, CodexNodeFactory codexNodeFactory) + private CodexNodeGroup CreateCodexGroup(CoreInterface coreInterface, ICodexInstance[] instances, CodexNodeFactory codexNodeFactory) { - var instances = runningContainers.Select(CreateInstance).ToArray(); var nodes = instances.Select(codexNodeFactory.CreateCodexNode).ToArray(); var group = new CodexNodeGroup(pluginTools, nodes); @@ -126,7 +121,7 @@ namespace CodexPlugin } catch { - CodexNodesNotOnline(coreInterface, runningContainers); + CodexNodesNotOnline(coreInterface, instances); throw; } @@ -136,14 +131,23 @@ namespace CodexPlugin private ICodexInstance CreateInstance(RunningPod pod) { var instance = CodexInstanceContainerExtension.CreateFromPod(pod); - podMap.Add(instance, pod); + var processControl = new CodexContainerProcessControl(pluginTools, pod, onStop: () => + { + processControlMap.Remove(instance.Name); + }); + processControlMap.Add(instance.Name, processControl); return instance; } - private void CodexNodesNotOnline(CoreInterface coreInterface, RunningPod[] runningContainers) + private void CodexNodesNotOnline(CoreInterface coreInterface, ICodexInstance[] instances) { Log("Codex nodes failed to start"); - foreach (var container in runningContainers.First().Containers) coreInterface.DownloadLog(container); + var log = pluginTools.GetLog(); + foreach (var i in instances) + { + var pc = processControlMap[i.Name]; + pc.DownloadLog(log.CreateSubfile(i.Name + "_failed_to_start")); + } } private void LogSeparator() diff --git a/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs b/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs index 91783e42..3542e52c 100644 --- a/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs +++ b/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs @@ -1,20 +1,19 @@ using CodexClient; using CodexClient.Hooks; using Core; -using KubernetesWorkflow.Types; namespace CodexPlugin { public static class CoreInterfaceExtensions { - public static RunningPod[] DeployCodexNodes(this CoreInterface ci, int number, Action setup) + public static ICodexInstance[] DeployCodexNodes(this CoreInterface ci, int number, Action setup) { return Plugin(ci).DeployCodexNodes(number, setup); } - public static ICodexNodeGroup WrapCodexContainers(this CoreInterface ci, RunningPod[] containers) + public static ICodexNodeGroup WrapCodexContainers(this CoreInterface ci, ICodexInstance[] instances) { - return Plugin(ci).WrapCodexContainers(ci, containers); + return Plugin(ci).WrapCodexContainers(ci, instances); } public static ICodexNode StartCodexNode(this CoreInterface ci) From d6afc6874b041511771f937fc416741c0979a15c Mon Sep 17 00:00:00 2001 From: ThatBen Date: Mon, 27 Jan 2025 09:59:19 +0100 Subject: [PATCH 15/25] Applies codexStarter interface --- ProjectPlugins/CodexPlugin/CodexPlugin.cs | 11 +++++----- ...dexStarter.cs => ContainerCodexStarter.cs} | 21 +++++++++---------- .../CodexPlugin/CoreInterfaceExtensions.cs | 2 +- ProjectPlugins/CodexPlugin/ICodexStarter.cs | 12 +++++++++++ 4 files changed, 29 insertions(+), 17 deletions(-) rename ProjectPlugins/CodexPlugin/{CodexStarter.cs => ContainerCodexStarter.cs} (90%) create mode 100644 ProjectPlugins/CodexPlugin/ICodexStarter.cs diff --git a/ProjectPlugins/CodexPlugin/CodexPlugin.cs b/ProjectPlugins/CodexPlugin/CodexPlugin.cs index 57e68d09..5c33754e 100644 --- a/ProjectPlugins/CodexPlugin/CodexPlugin.cs +++ b/ProjectPlugins/CodexPlugin/CodexPlugin.cs @@ -6,13 +6,14 @@ namespace CodexPlugin { public class CodexPlugin : IProjectPlugin, IHasLogPrefix, IHasMetadata { - private readonly CodexStarter codexStarter; + private readonly ICodexStarter codexStarter; private readonly IPluginTools tools; private readonly CodexLogLevel defaultLogLevel = CodexLogLevel.Trace; + private readonly CodexHooksFactory hooksFactory = new CodexHooksFactory(); public CodexPlugin(IPluginTools tools) { - codexStarter = new CodexStarter(tools); + codexStarter = new ContainerCodexStarter(tools, hooksFactory); this.tools = tools; } @@ -39,10 +40,10 @@ namespace CodexPlugin return codexStarter.BringOnline(codexSetup); } - public ICodexNodeGroup WrapCodexContainers(CoreInterface coreInterface, ICodexInstance[] instances) + public ICodexNodeGroup WrapCodexContainers(ICodexInstance[] instances) { instances = instances.Select(c => SerializeGate.Gate(c as CodexInstance)).ToArray(); - return codexStarter.WrapCodexContainers(coreInterface, instances); + return codexStarter.WrapCodexContainers(instances); } public void WireUpMarketplace(ICodexNodeGroup result, Action setup) @@ -64,7 +65,7 @@ namespace CodexPlugin public void SetCodexHooksProvider(ICodexHooksProvider hooksProvider) { - codexStarter.HooksFactory.Provider = hooksProvider; + hooksFactory.Provider = hooksProvider; } private CodexSetup GetSetup(int numberOfNodes, Action setup) diff --git a/ProjectPlugins/CodexPlugin/CodexStarter.cs b/ProjectPlugins/CodexPlugin/ContainerCodexStarter.cs similarity index 90% rename from ProjectPlugins/CodexPlugin/CodexStarter.cs rename to ProjectPlugins/CodexPlugin/ContainerCodexStarter.cs index 08c31efd..f40d8115 100644 --- a/ProjectPlugins/CodexPlugin/CodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/ContainerCodexStarter.cs @@ -8,23 +8,22 @@ using Utils; namespace CodexPlugin { - public class CodexStarter : IProcessControlFactory + public class ContainerCodexStarter : ICodexStarter { private readonly IPluginTools pluginTools; + private readonly CodexHooksFactory hooksFactory; private readonly CodexContainerRecipe recipe = new CodexContainerRecipe(); private readonly ApiChecker apiChecker; private readonly Dictionary processControlMap = new Dictionary(); private DebugInfoVersion? versionResponse; - public CodexStarter(IPluginTools pluginTools) + public ContainerCodexStarter(IPluginTools pluginTools, CodexHooksFactory hooksFactory) { this.pluginTools = pluginTools; - + this.hooksFactory = hooksFactory; apiChecker = new ApiChecker(pluginTools); } - public CodexHooksFactory HooksFactory { get; } = new CodexHooksFactory(); - public IProcessControl CreateProcessControl(ICodexInstance instance) { return processControlMap[instance.Name]; @@ -53,16 +52,16 @@ namespace CodexPlugin return containers.Select(CreateInstance).ToArray(); } - public ICodexNodeGroup WrapCodexContainers(CoreInterface coreInterface, ICodexInstance[] instances) + public ICodexNodeGroup WrapCodexContainers(ICodexInstance[] instances) { var codexNodeFactory = new CodexNodeFactory( log: pluginTools.GetLog(), fileManager: pluginTools.GetFileManager(), - hooksFactory: HooksFactory, + hooksFactory: hooksFactory, httpFactory: pluginTools, processControlFactory: this); - var group = CreateCodexGroup(coreInterface, instances, codexNodeFactory); + var group = CreateCodexGroup(instances, codexNodeFactory); Log($"Codex version: {group.Version}"); versionResponse = group.Version; @@ -110,7 +109,7 @@ namespace CodexPlugin return workflow.GetPodInfo(rc); } - private CodexNodeGroup CreateCodexGroup(CoreInterface coreInterface, ICodexInstance[] instances, CodexNodeFactory codexNodeFactory) + private CodexNodeGroup CreateCodexGroup(ICodexInstance[] instances, CodexNodeFactory codexNodeFactory) { var nodes = instances.Select(codexNodeFactory.CreateCodexNode).ToArray(); var group = new CodexNodeGroup(pluginTools, nodes); @@ -121,7 +120,7 @@ namespace CodexPlugin } catch { - CodexNodesNotOnline(coreInterface, instances); + CodexNodesNotOnline(instances); throw; } @@ -139,7 +138,7 @@ namespace CodexPlugin return instance; } - private void CodexNodesNotOnline(CoreInterface coreInterface, ICodexInstance[] instances) + private void CodexNodesNotOnline(ICodexInstance[] instances) { Log("Codex nodes failed to start"); var log = pluginTools.GetLog(); diff --git a/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs b/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs index 3542e52c..06b9246e 100644 --- a/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs +++ b/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs @@ -13,7 +13,7 @@ namespace CodexPlugin public static ICodexNodeGroup WrapCodexContainers(this CoreInterface ci, ICodexInstance[] instances) { - return Plugin(ci).WrapCodexContainers(ci, instances); + return Plugin(ci).WrapCodexContainers(instances); } public static ICodexNode StartCodexNode(this CoreInterface ci) diff --git a/ProjectPlugins/CodexPlugin/ICodexStarter.cs b/ProjectPlugins/CodexPlugin/ICodexStarter.cs new file mode 100644 index 00000000..5becf6a3 --- /dev/null +++ b/ProjectPlugins/CodexPlugin/ICodexStarter.cs @@ -0,0 +1,12 @@ +using CodexClient; + +namespace CodexPlugin +{ + public interface ICodexStarter : IProcessControlFactory + { + string GetCodexId(); + string GetCodexRevision(); + ICodexInstance[] BringOnline(CodexSetup codexSetup); + ICodexNodeGroup WrapCodexContainers(ICodexInstance[] instances); + } +} From 2c549c5410629cf179544679f76694957c45a4cd Mon Sep 17 00:00:00 2001 From: ThatBen Date: Mon, 27 Jan 2025 10:22:34 +0100 Subject: [PATCH 16/25] extracts codex wrapper --- .../CodexPlugin/BinaryCodexStarter.cs | 12 ++ .../CodexContainerProcessControl.cs | 70 +++++++++ ProjectPlugins/CodexPlugin/CodexPlugin.cs | 13 +- ProjectPlugins/CodexPlugin/CodexWrapper.cs | 80 ++++++++++ .../CodexPlugin/ContainerCodexStarter.cs | 139 +----------------- ProjectPlugins/CodexPlugin/ICodexStarter.cs | 5 +- .../CodexPlugin/ProcessControlMap.cs | 29 ++++ 7 files changed, 205 insertions(+), 143 deletions(-) create mode 100644 ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs create mode 100644 ProjectPlugins/CodexPlugin/CodexContainerProcessControl.cs create mode 100644 ProjectPlugins/CodexPlugin/CodexWrapper.cs create mode 100644 ProjectPlugins/CodexPlugin/ProcessControlMap.cs diff --git a/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs b/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs new file mode 100644 index 00000000..4353c818 --- /dev/null +++ b/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs @@ -0,0 +1,12 @@ +using CodexClient; + +namespace CodexPlugin +{ + public class BinaryCodexStarter : ICodexStarter + { + public ICodexInstance[] BringOnline(CodexSetup codexSetup) + { + throw new NotImplementedException(); + } + } +} diff --git a/ProjectPlugins/CodexPlugin/CodexContainerProcessControl.cs b/ProjectPlugins/CodexPlugin/CodexContainerProcessControl.cs new file mode 100644 index 00000000..a6afc2fe --- /dev/null +++ b/ProjectPlugins/CodexPlugin/CodexContainerProcessControl.cs @@ -0,0 +1,70 @@ +using CodexClient; +using Core; +using KubernetesWorkflow; +using KubernetesWorkflow.Types; +using Logging; + +namespace CodexPlugin +{ + public class CodexContainerProcessControl : IProcessControl + { + private readonly IPluginTools tools; + private readonly RunningPod pod; + private readonly Action onStop; + private readonly ContainerCrashWatcher crashWatcher; + + public CodexContainerProcessControl(IPluginTools tools, RunningPod pod, Action onStop) + { + this.tools = tools; + this.pod = pod; + this.onStop = onStop; + + crashWatcher = tools.CreateWorkflow().CreateCrashWatcher(pod.Containers.Single()); + crashWatcher.Start(); + } + + public void Stop(bool waitTillStopped) + { + Log($"Stopping node..."); + var workflow = tools.CreateWorkflow(); + workflow.Stop(pod, waitTillStopped); + crashWatcher.Stop(); + onStop(); + Log("Stopped."); + } + + public IDownloadedLog DownloadLog(LogFile file) + { + var workflow = tools.CreateWorkflow(); + return workflow.DownloadContainerLog(pod.Containers.Single()); + } + + public void DeleteDataDirFolder() + { + var container = pod.Containers.Single(); + + 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("Deleted repo folder."); + } + catch (Exception e) + { + Log("Unable to delete repo folder: " + e); + } + } + + public bool HasCrashed() + { + return crashWatcher.HasCrashed(); + } + + private void Log(string message) + { + tools.GetLog().Log(message); + } + } +} diff --git a/ProjectPlugins/CodexPlugin/CodexPlugin.cs b/ProjectPlugins/CodexPlugin/CodexPlugin.cs index 5c33754e..e6c56f7c 100644 --- a/ProjectPlugins/CodexPlugin/CodexPlugin.cs +++ b/ProjectPlugins/CodexPlugin/CodexPlugin.cs @@ -10,10 +10,13 @@ namespace CodexPlugin private readonly IPluginTools tools; private readonly CodexLogLevel defaultLogLevel = CodexLogLevel.Trace; private readonly CodexHooksFactory hooksFactory = new CodexHooksFactory(); + private readonly ProcessControlMap processControlMap = new ProcessControlMap(); + private readonly CodexWrapper codexWrapper; public CodexPlugin(IPluginTools tools) { - codexStarter = new ContainerCodexStarter(tools, hooksFactory); + codexStarter = new ContainerCodexStarter(tools, processControlMap); + codexWrapper = new CodexWrapper(tools, processControlMap, hooksFactory); this.tools = tools; } @@ -21,13 +24,13 @@ namespace CodexPlugin public void Announce() { - Log($"Loaded with Codex ID: '{codexStarter.GetCodexId()}' - Revision: {codexStarter.GetCodexRevision()}"); + Log($"Loaded with Codex ID: '{codexWrapper.GetCodexId()}' - Revision: {codexWrapper.GetCodexRevision()}"); } public void AddMetadata(IAddMetadata metadata) { - metadata.Add("codexid", codexStarter.GetCodexId()); - metadata.Add("codexrevision", codexStarter.GetCodexRevision()); + metadata.Add("codexid", codexWrapper.GetCodexId()); + metadata.Add("codexrevision", codexWrapper.GetCodexRevision()); } public void Decommission() @@ -43,7 +46,7 @@ namespace CodexPlugin public ICodexNodeGroup WrapCodexContainers(ICodexInstance[] instances) { instances = instances.Select(c => SerializeGate.Gate(c as CodexInstance)).ToArray(); - return codexStarter.WrapCodexContainers(instances); + return codexWrapper.WrapCodexInstances(instances); } public void WireUpMarketplace(ICodexNodeGroup result, Action setup) diff --git a/ProjectPlugins/CodexPlugin/CodexWrapper.cs b/ProjectPlugins/CodexPlugin/CodexWrapper.cs new file mode 100644 index 00000000..a4a4eda6 --- /dev/null +++ b/ProjectPlugins/CodexPlugin/CodexWrapper.cs @@ -0,0 +1,80 @@ +using CodexClient; +using CodexClient.Hooks; +using Core; +using Logging; + +namespace CodexPlugin +{ + public class CodexWrapper + { + private readonly IPluginTools pluginTools; + private readonly ProcessControlMap processControlMap; + private readonly CodexHooksFactory hooksFactory; + private DebugInfoVersion? versionResponse; + + public CodexWrapper(IPluginTools pluginTools, ProcessControlMap processControlMap, CodexHooksFactory hooksFactory) + { + this.pluginTools = pluginTools; + this.processControlMap = processControlMap; + this.hooksFactory = hooksFactory; + } + + public string GetCodexId() + { + if (versionResponse != null) return versionResponse.Version; + return "unknown"; + } + + public string GetCodexRevision() + { + if (versionResponse != null) return versionResponse.Revision; + return "unknown"; + } + + public ICodexNodeGroup WrapCodexInstances(ICodexInstance[] instances) + { + var codexNodeFactory = new CodexNodeFactory( + log: pluginTools.GetLog(), + fileManager: pluginTools.GetFileManager(), + hooksFactory: hooksFactory, + httpFactory: pluginTools, + processControlFactory: processControlMap); + + var group = CreateCodexGroup(instances, codexNodeFactory); + + pluginTools.GetLog().Log($"Codex version: {group.Version}"); + versionResponse = group.Version; + + return group; + } + + private CodexNodeGroup CreateCodexGroup(ICodexInstance[] instances, CodexNodeFactory codexNodeFactory) + { + var nodes = instances.Select(codexNodeFactory.CreateCodexNode).ToArray(); + var group = new CodexNodeGroup(pluginTools, nodes); + + try + { + Stopwatch.Measure(pluginTools.GetLog(), "EnsureOnline", group.EnsureOnline); + } + catch + { + CodexNodesNotOnline(instances); + throw; + } + + return group; + } + + private void CodexNodesNotOnline(ICodexInstance[] instances) + { + pluginTools.GetLog().Log("Codex nodes failed to start"); + var log = pluginTools.GetLog(); + foreach (var i in instances) + { + var pc = processControlMap.Get(i); + pc.DownloadLog(log.CreateSubfile(i.Name + "_failed_to_start")); + } + } + } +} diff --git a/ProjectPlugins/CodexPlugin/ContainerCodexStarter.cs b/ProjectPlugins/CodexPlugin/ContainerCodexStarter.cs index f40d8115..658641b6 100644 --- a/ProjectPlugins/CodexPlugin/ContainerCodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/ContainerCodexStarter.cs @@ -1,9 +1,7 @@ using CodexClient; -using CodexClient.Hooks; using Core; using KubernetesWorkflow; using KubernetesWorkflow.Types; -using Logging; using Utils; namespace CodexPlugin @@ -11,24 +9,17 @@ namespace CodexPlugin public class ContainerCodexStarter : ICodexStarter { private readonly IPluginTools pluginTools; - private readonly CodexHooksFactory hooksFactory; + private readonly ProcessControlMap processControlMap; private readonly CodexContainerRecipe recipe = new CodexContainerRecipe(); private readonly ApiChecker apiChecker; - private readonly Dictionary processControlMap = new Dictionary(); - private DebugInfoVersion? versionResponse; - public ContainerCodexStarter(IPluginTools pluginTools, CodexHooksFactory hooksFactory) + public ContainerCodexStarter(IPluginTools pluginTools, ProcessControlMap processControlMap) { this.pluginTools = pluginTools; - this.hooksFactory = hooksFactory; + this.processControlMap = processControlMap; apiChecker = new ApiChecker(pluginTools); } - public IProcessControl CreateProcessControl(ICodexInstance instance) - { - return processControlMap[instance.Name]; - } - public ICodexInstance[] BringOnline(CodexSetup codexSetup) { LogSeparator(); @@ -52,35 +43,6 @@ namespace CodexPlugin return containers.Select(CreateInstance).ToArray(); } - public ICodexNodeGroup WrapCodexContainers(ICodexInstance[] instances) - { - var codexNodeFactory = new CodexNodeFactory( - log: pluginTools.GetLog(), - fileManager: pluginTools.GetFileManager(), - hooksFactory: hooksFactory, - httpFactory: pluginTools, - processControlFactory: this); - - var group = CreateCodexGroup(instances, codexNodeFactory); - - Log($"Codex version: {group.Version}"); - versionResponse = group.Version; - - return group; - } - - public string GetCodexId() - { - if (versionResponse != null) return versionResponse.Version; - return recipe.Image; - } - - public string GetCodexRevision() - { - if (versionResponse != null) return versionResponse.Revision; - return "unknown"; - } - private StartupConfig CreateStartupConfig(CodexSetup codexSetup) { var startupConfig = new StartupConfig(); @@ -109,46 +71,17 @@ namespace CodexPlugin return workflow.GetPodInfo(rc); } - private CodexNodeGroup CreateCodexGroup(ICodexInstance[] instances, CodexNodeFactory codexNodeFactory) - { - var nodes = instances.Select(codexNodeFactory.CreateCodexNode).ToArray(); - var group = new CodexNodeGroup(pluginTools, nodes); - - try - { - Stopwatch.Measure(pluginTools.GetLog(), "EnsureOnline", group.EnsureOnline); - } - catch - { - CodexNodesNotOnline(instances); - throw; - } - - return group; - } - private ICodexInstance CreateInstance(RunningPod pod) { var instance = CodexInstanceContainerExtension.CreateFromPod(pod); var processControl = new CodexContainerProcessControl(pluginTools, pod, onStop: () => { - processControlMap.Remove(instance.Name); + processControlMap.Remove(instance); }); - processControlMap.Add(instance.Name, processControl); + processControlMap.Add(instance, processControl); return instance; } - private void CodexNodesNotOnline(ICodexInstance[] instances) - { - Log("Codex nodes failed to start"); - var log = pluginTools.GetLog(); - foreach (var i in instances) - { - var pc = processControlMap[i.Name]; - pc.DownloadLog(log.CreateSubfile(i.Name + "_failed_to_start")); - } - } - private void LogSeparator() { Log("----------------------------------------------------------------------------"); @@ -166,66 +99,4 @@ namespace CodexPlugin pluginTools.GetLog().Log(message); } } - - public class CodexContainerProcessControl : IProcessControl - { - private readonly IPluginTools tools; - private readonly RunningPod pod; - private readonly Action onStop; - private readonly ContainerCrashWatcher crashWatcher; - - public CodexContainerProcessControl(IPluginTools tools, RunningPod pod, Action onStop) - { - this.tools = tools; - this.pod = pod; - this.onStop = onStop; - - crashWatcher = tools.CreateWorkflow().CreateCrashWatcher(pod.Containers.Single()); - crashWatcher.Start(); - } - - public void Stop(bool waitTillStopped) - { - Log($"Stopping node..."); - var workflow = tools.CreateWorkflow(); - workflow.Stop(pod, waitTillStopped); - crashWatcher.Stop(); - onStop(); - Log("Stopped."); - } - - public IDownloadedLog DownloadLog(LogFile file) - { - var workflow = tools.CreateWorkflow(); - return workflow.DownloadContainerLog(pod.Containers.Single()); - } - - public void DeleteDataDirFolder() - { - var container = pod.Containers.Single(); - - 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("Deleted repo folder."); - } - catch (Exception e) - { - Log("Unable to delete repo folder: " + e); - } - } - - public bool HasCrashed() - { - return crashWatcher.HasCrashed(); - } - - private void Log(string message) - { - tools.GetLog().Log(message); - } - } } diff --git a/ProjectPlugins/CodexPlugin/ICodexStarter.cs b/ProjectPlugins/CodexPlugin/ICodexStarter.cs index 5becf6a3..4f264342 100644 --- a/ProjectPlugins/CodexPlugin/ICodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/ICodexStarter.cs @@ -2,11 +2,8 @@ namespace CodexPlugin { - public interface ICodexStarter : IProcessControlFactory + public interface ICodexStarter { - string GetCodexId(); - string GetCodexRevision(); ICodexInstance[] BringOnline(CodexSetup codexSetup); - ICodexNodeGroup WrapCodexContainers(ICodexInstance[] instances); } } diff --git a/ProjectPlugins/CodexPlugin/ProcessControlMap.cs b/ProjectPlugins/CodexPlugin/ProcessControlMap.cs new file mode 100644 index 00000000..a91cce99 --- /dev/null +++ b/ProjectPlugins/CodexPlugin/ProcessControlMap.cs @@ -0,0 +1,29 @@ +using CodexClient; + +namespace CodexPlugin +{ + public class ProcessControlMap : IProcessControlFactory + { + private readonly Dictionary processControlMap = new Dictionary(); + + public void Add(ICodexInstance instance, IProcessControl control) + { + processControlMap.Add(instance.Name, control); + } + + public void Remove(ICodexInstance instance) + { + processControlMap.Remove(instance.Name); + } + + public IProcessControl CreateProcessControl(ICodexInstance instance) + { + return Get(instance); + } + + public IProcessControl Get(ICodexInstance instance) + { + return processControlMap[instance.Name]; + } + } +} From e4512438ed8b9a41130914fae39eb5d18d585d7f Mon Sep 17 00:00:00 2001 From: ThatBen Date: Mon, 27 Jan 2025 15:45:46 +0100 Subject: [PATCH 17/25] working two-client test with binary instances --- .../CodexPlugin/BinaryCodexStarter.cs | 79 ++++++++- .../CodexPlugin/BinaryProcessControl.cs | 56 ++++++ ProjectPlugins/CodexPlugin/CodexPlugin.cs | 3 +- .../CodexPlugin/CodexProcessRecipe.cs | 163 ++++++++++++++++++ ProjectPlugins/CodexPlugin/FreePortFinder.cs | 43 +++++ 5 files changed, 342 insertions(+), 2 deletions(-) create mode 100644 ProjectPlugins/CodexPlugin/BinaryProcessControl.cs create mode 100644 ProjectPlugins/CodexPlugin/CodexProcessRecipe.cs create mode 100644 ProjectPlugins/CodexPlugin/FreePortFinder.cs diff --git a/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs b/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs index 4353c818..365c2e8b 100644 --- a/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs @@ -1,12 +1,89 @@ using CodexClient; +using Core; +using Utils; +using System.Diagnostics; namespace CodexPlugin { public class BinaryCodexStarter : ICodexStarter { + private readonly IPluginTools pluginTools; + private readonly ProcessControlMap processControlMap; + private readonly NumberSource numberSource = new NumberSource(1); + private readonly FreePortFinder freePortFinder = new FreePortFinder(); + + public BinaryCodexStarter(IPluginTools pluginTools, ProcessControlMap processControlMap) + { + this.pluginTools = pluginTools; + this.processControlMap = processControlMap; + } + public ICodexInstance[] BringOnline(CodexSetup codexSetup) { - throw new NotImplementedException(); + LogSeparator(); + Log($"Starting {codexSetup.Describe()}..."); + + return StartCodexBinaries(codexSetup, codexSetup.NumberOfNodes); + } + + private ICodexInstance[] StartCodexBinaries(CodexStartupConfig startupConfig, int numberOfNodes) + { + var result = new List(); + for (var i = 0; i < numberOfNodes; i++) + { + result.Add(StartBinary(startupConfig)); + } + + return result.ToArray(); + } + + private ICodexInstance StartBinary(CodexStartupConfig config) + { + var portMap = new CodexPortMap(freePortFinder); + var factory = new CodexProcessRecipe(portMap, numberSource); + var recipe = factory.Initialize(config); + + var name = "codex_" + numberSource.GetNextNumber(); + var startInfo = new ProcessStartInfo( + fileName: recipe.Cmd, + arguments: recipe.Args + ); + //startInfo.UseShellExecute = true; + startInfo.RedirectStandardOutput = true; + startInfo.RedirectStandardError = true; + + var process = Process.Start(startInfo); + if (process == null || process.HasExited) + { + throw new Exception("Failed to start"); + } + + var local = "localhost"; + var instance = new CodexInstance( + name: name, + imageName: "binary", + startUtc: DateTime.UtcNow, + discoveryEndpoint: new Address("Disc", local, portMap.DiscPort), + apiEndpoint: new Address("Api", "http://" + local, portMap.ApiPort), + listenEndpoint: new Address("Listen", local, portMap.ListenPort), + ethAccount: null, + metricsEndpoint: null + ); + + var pc = new BinaryProcessControl(process, name); + processControlMap.Add(instance, pc); + + return instance; + } + + private void LogSeparator() + { + Log("----------------------------------------------------------------------------"); + } + + private void Log(string message) + { + pluginTools.GetLog().Log(message); } } } diff --git a/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs b/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs new file mode 100644 index 00000000..0e13fb35 --- /dev/null +++ b/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs @@ -0,0 +1,56 @@ +using System.Diagnostics; +using CodexClient; +using Logging; + +namespace CodexPlugin +{ + public class BinaryProcessControl : IProcessControl + { + private Process process; + private readonly string nodeName; + private bool running; + private readonly List logLines = new List(); + private readonly List streamTasks = new List(); + + public BinaryProcessControl(Process process, string nodeName) + { + running = true; + this.process = process; + this.nodeName = nodeName; + streamTasks.Add(Task.Run(() => ReadProcessStream(process.StandardOutput))); + streamTasks.Add(Task.Run(() => ReadProcessStream(process.StandardError))); + } + + private void ReadProcessStream(StreamReader reader) + { + while (running) + { + var line = reader.ReadLine(); + if (!string.IsNullOrEmpty(line)) logLines.Add(line); + } + } + + public void DeleteDataDirFolder() + { + throw new NotImplementedException(); + } + + public IDownloadedLog DownloadLog(LogFile file) + { + foreach (var line in logLines) file.WriteRaw(line); + return new DownloadedLog(file, nodeName); + } + + public bool HasCrashed() + { + return false; + } + + public void Stop(bool waitTillStopped) + { + running = false; + process.Kill(); + foreach (var t in streamTasks) t.Wait(); + } + } +} diff --git a/ProjectPlugins/CodexPlugin/CodexPlugin.cs b/ProjectPlugins/CodexPlugin/CodexPlugin.cs index e6c56f7c..1ebe6d77 100644 --- a/ProjectPlugins/CodexPlugin/CodexPlugin.cs +++ b/ProjectPlugins/CodexPlugin/CodexPlugin.cs @@ -15,7 +15,8 @@ namespace CodexPlugin public CodexPlugin(IPluginTools tools) { - codexStarter = new ContainerCodexStarter(tools, processControlMap); + //codexStarter = new ContainerCodexStarter(tools, processControlMap); + codexStarter = new BinaryCodexStarter(tools, processControlMap); codexWrapper = new CodexWrapper(tools, processControlMap, hooksFactory); this.tools = tools; } diff --git a/ProjectPlugins/CodexPlugin/CodexProcessRecipe.cs b/ProjectPlugins/CodexPlugin/CodexProcessRecipe.cs new file mode 100644 index 00000000..f439d640 --- /dev/null +++ b/ProjectPlugins/CodexPlugin/CodexProcessRecipe.cs @@ -0,0 +1,163 @@ +using System.Net.Sockets; +using System.Net; +using Utils; +using Nethereum.Util; + +namespace CodexPlugin +{ + public class ProcessRecipe + { + public ProcessRecipe(string cmd, string[] args) + { + Cmd = cmd; + Args = args; + } + + public string Cmd { get; } + public string[] Args { get; } + } + + public class CodexPortMap + { + public CodexPortMap(FreePortFinder freePortFinder) + { + ApiPort = freePortFinder.GetNextFreePort(); + DiscPort = freePortFinder.GetNextFreePort(); + ListenPort = freePortFinder.GetNextFreePort(); + } + + public int ApiPort { get; } + public int DiscPort { get; } + public int ListenPort { get; } + } + + public class CodexProcessRecipe + { + private readonly CodexPortMap portMap; + private readonly NumberSource numberSource; + + public CodexProcessRecipe(CodexPortMap portMap, NumberSource numberSource) + { + this.portMap = portMap; + this.numberSource = numberSource; + } + + public ProcessRecipe Initialize(CodexStartupConfig config) + { + args.Clear(); + + AddArg("--api-port", portMap.ApiPort); + AddArg("--api-bindaddr", "0.0.0.0"); + + var dataDir = $"datadir_{numberSource.GetNextNumber()}"; + AddArg("--data-dir", dataDir); + + AddArg("--disc-port", portMap.DiscPort); + AddArg("--log-level", config.LogLevelWithTopics()); + + // This makes the node announce itself to its local IP address. + var host = Dns.GetHostEntry(Dns.GetHostName()); + var addrs = host.AddressList.Where(a => a.AddressFamily == AddressFamily.InterNetwork).ToList(); + var ipaddrs = addrs.First(); + + AddArg("--nat", $"extip:{ipaddrs.ToStringInvariant()}"); + + AddArg("--listen-addrs", $"/ip4/0.0.0.0/tcp/{portMap.ListenPort}"); + + if (!string.IsNullOrEmpty(config.BootstrapSpr)) + { + AddArg("--bootstrap-node", config.BootstrapSpr); + } + if (config.StorageQuota != null) + { + AddArg("--storage-quota", config.StorageQuota.SizeInBytes.ToString()!); + } + if (config.BlockTTL != null) + { + AddArg("--block-ttl", config.BlockTTL.ToString()!); + } + if (config.BlockMaintenanceInterval != null) + { + AddArg("--block-mi", Convert.ToInt32(config.BlockMaintenanceInterval.Value.TotalSeconds).ToString()); + } + if (config.BlockMaintenanceNumber != null) + { + AddArg("--block-mn", config.BlockMaintenanceNumber.ToString()!); + } + if (config.MetricsEnabled) + { + throw new Exception("Not supported"); + //var metricsPort = CreateApiPort(config, MetricsPortTag); + //AddEnvVar("CODEX_METRICS", "true"); + //AddEnvVar("CODEX_METRICS_ADDRESS", "0.0.0.0"); + //AddEnvVar("CODEX_METRICS_PORT", metricsPort); + //AddPodAnnotation("prometheus.io/scrape", "true"); + //AddPodAnnotation("prometheus.io/port", metricsPort.Number.ToString()); + } + + if (config.SimulateProofFailures != null) + { + throw new Exception("Not supported"); + //AddEnvVar("CODEX_SIMULATE_PROOF_FAILURES", config.SimulateProofFailures.ToString()!); + } + + if (config.MarketplaceConfig != null) + { + throw new Exception("Not supported"); + //var mconfig = config.MarketplaceConfig; + //var gethStart = mconfig.GethNode.StartResult; + //var wsAddress = gethStart.Container.GetInternalAddress(GethContainerRecipe.WsPortTag); + //var marketplaceAddress = mconfig.CodexContracts.Deployment.MarketplaceAddress; + + //AddEnvVar("CODEX_ETH_PROVIDER", $"{wsAddress.Host.Replace("http://", "ws://")}:{wsAddress.Port}"); + //AddEnvVar("CODEX_MARKETPLACE_ADDRESS", marketplaceAddress); + + //var marketplaceSetup = config.MarketplaceConfig.MarketplaceSetup; + + //// Custom scripting in the Codex test image will write this variable to a private-key file, + //// and pass the correct filename to Codex. + //var account = marketplaceSetup.EthAccountSetup.GetNew(); + //AddEnvVar("ETH_PRIVATE_KEY", account.PrivateKey); + //Additional(account); + + //SetCommandOverride(marketplaceSetup); + //if (marketplaceSetup.IsValidator) + //{ + // AddEnvVar("CODEX_VALIDATOR", "true"); + //} + } + + //if (!string.IsNullOrEmpty(config.NameOverride)) + //{ + // AddEnvVar("CODEX_NODENAME", config.NameOverride); + //} + + return Create(); + } + + private ProcessRecipe Create() + { + return new ProcessRecipe( + cmd: Path.Combine( + "d:", + "Dev", + "nim-codex", + "build", + "codex.exe" + ), + args: args.ToArray()); + } + + private readonly List args = new List(); + + private void AddArg(string arg, string val) + { + args.Add($"{arg}={val}"); + } + + private void AddArg(string arg, int val) + { + args.Add($"{arg}={val}"); + } + } +} diff --git a/ProjectPlugins/CodexPlugin/FreePortFinder.cs b/ProjectPlugins/CodexPlugin/FreePortFinder.cs new file mode 100644 index 00000000..9a67899d --- /dev/null +++ b/ProjectPlugins/CodexPlugin/FreePortFinder.cs @@ -0,0 +1,43 @@ +using System.Net.NetworkInformation; + +namespace CodexPlugin +{ + public class FreePortFinder + { + private readonly object _lock = new object(); + private int nextPort = 8080; + + public int GetNextFreePort() + { + lock (_lock) + { + return Next(); + } + } + + private int Next() + { + while (true) + { + var p = nextPort; + nextPort++; + + if (!IsInUse(p)) + { + return p; + } + + if (nextPort > 30000) throw new Exception("Running out of ports."); + } + } + + private bool IsInUse(int port) + { + var ipProps = IPGlobalProperties.GetIPGlobalProperties(); + if (ipProps.GetActiveTcpConnections().Any(t => t.LocalEndPoint.Port == port)) return true; + if (ipProps.GetActiveTcpListeners().Any(t => t.Port == port)) return true; + if (ipProps.GetActiveUdpListeners().Any(u => u.Port == port)) return true; + return false; + } + } +} From b1197975a15548eae4005d603757ab40298f0564 Mon Sep 17 00:00:00 2001 From: ThatBen Date: Tue, 28 Jan 2025 11:21:47 +0100 Subject: [PATCH 18/25] cleanup process and datadir after test stop --- .../CodexPlugin/BinaryCodexStarter.cs | 20 +++++++++------ .../CodexPlugin/BinaryProcessControl.cs | 23 +++++++++++------ ProjectPlugins/CodexPlugin/CodexPlugin.cs | 1 + .../CodexPlugin/CodexProcessRecipe.cs | 25 ++++++++++--------- .../CodexPlugin/ContainerCodexStarter.cs | 4 +++ ProjectPlugins/CodexPlugin/ICodexStarter.cs | 1 + .../CodexPlugin/ProcessControlMap.cs | 8 ++++++ 7 files changed, 55 insertions(+), 27 deletions(-) diff --git a/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs b/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs index 365c2e8b..64eb67b2 100644 --- a/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs @@ -26,6 +26,11 @@ namespace CodexPlugin return StartCodexBinaries(codexSetup, codexSetup.NumberOfNodes); } + public void Decommission() + { + processControlMap.StopAll(); + } + private ICodexInstance[] StartCodexBinaries(CodexStartupConfig startupConfig, int numberOfNodes) { var result = new List(); @@ -39,11 +44,12 @@ namespace CodexPlugin private ICodexInstance StartBinary(CodexStartupConfig config) { - var portMap = new CodexPortMap(freePortFinder); - var factory = new CodexProcessRecipe(portMap, numberSource); + var name = "codex_" + numberSource.GetNextNumber(); + var dataDir = $"datadir_{numberSource.GetNextNumber()}"; + var pconfig = new CodexProcessConfig(name, freePortFinder, dataDir); + var factory = new CodexProcessRecipe(pconfig); var recipe = factory.Initialize(config); - var name = "codex_" + numberSource.GetNextNumber(); var startInfo = new ProcessStartInfo( fileName: recipe.Cmd, arguments: recipe.Args @@ -63,14 +69,14 @@ namespace CodexPlugin name: name, imageName: "binary", startUtc: DateTime.UtcNow, - discoveryEndpoint: new Address("Disc", local, portMap.DiscPort), - apiEndpoint: new Address("Api", "http://" + local, portMap.ApiPort), - listenEndpoint: new Address("Listen", local, portMap.ListenPort), + discoveryEndpoint: new Address("Disc", local, pconfig.DiscPort), + apiEndpoint: new Address("Api", "http://" + local, pconfig.ApiPort), + listenEndpoint: new Address("Listen", local, pconfig.ListenPort), ethAccount: null, metricsEndpoint: null ); - var pc = new BinaryProcessControl(process, name); + var pc = new BinaryProcessControl(process, pconfig); processControlMap.Add(instance, pc); return instance; diff --git a/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs b/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs index 0e13fb35..ce1fbec3 100644 --- a/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs +++ b/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs @@ -6,17 +6,17 @@ namespace CodexPlugin { public class BinaryProcessControl : IProcessControl { - private Process process; - private readonly string nodeName; - private bool running; + private readonly Process process; + private readonly CodexProcessConfig config; private readonly List logLines = new List(); private readonly List streamTasks = new List(); + private bool running; - public BinaryProcessControl(Process process, string nodeName) + public BinaryProcessControl(Process process, CodexProcessConfig config) { running = true; this.process = process; - this.nodeName = nodeName; + this.config = config; streamTasks.Add(Task.Run(() => ReadProcessStream(process.StandardOutput))); streamTasks.Add(Task.Run(() => ReadProcessStream(process.StandardError))); } @@ -32,13 +32,14 @@ namespace CodexPlugin public void DeleteDataDirFolder() { - throw new NotImplementedException(); + if (!Directory.Exists(config.DataDir)) throw new Exception("datadir not found"); + Directory.Delete(config.DataDir, true); } public IDownloadedLog DownloadLog(LogFile file) { foreach (var line in logLines) file.WriteRaw(line); - return new DownloadedLog(file, nodeName); + return new DownloadedLog(file, config.Name); } public bool HasCrashed() @@ -50,7 +51,13 @@ namespace CodexPlugin { running = false; process.Kill(); - foreach (var t in streamTasks) t.Wait(); + + if (waitTillStopped) + { + foreach (var t in streamTasks) t.Wait(); + } + + DeleteDataDirFolder(); } } } diff --git a/ProjectPlugins/CodexPlugin/CodexPlugin.cs b/ProjectPlugins/CodexPlugin/CodexPlugin.cs index 1ebe6d77..b208347c 100644 --- a/ProjectPlugins/CodexPlugin/CodexPlugin.cs +++ b/ProjectPlugins/CodexPlugin/CodexPlugin.cs @@ -36,6 +36,7 @@ namespace CodexPlugin public void Decommission() { + codexStarter.Decommission(); } public ICodexInstance[] DeployCodexNodes(int numberOfNodes, Action setup) diff --git a/ProjectPlugins/CodexPlugin/CodexProcessRecipe.cs b/ProjectPlugins/CodexPlugin/CodexProcessRecipe.cs index f439d640..98bb9dbf 100644 --- a/ProjectPlugins/CodexPlugin/CodexProcessRecipe.cs +++ b/ProjectPlugins/CodexPlugin/CodexProcessRecipe.cs @@ -17,42 +17,43 @@ namespace CodexPlugin public string[] Args { get; } } - public class CodexPortMap + public class CodexProcessConfig { - public CodexPortMap(FreePortFinder freePortFinder) + public CodexProcessConfig(string name, FreePortFinder freePortFinder, string dataDir) { ApiPort = freePortFinder.GetNextFreePort(); DiscPort = freePortFinder.GetNextFreePort(); ListenPort = freePortFinder.GetNextFreePort(); + Name = name; + DataDir = dataDir; } public int ApiPort { get; } public int DiscPort { get; } public int ListenPort { get; } + public string Name { get; } + public string DataDir { get; } } public class CodexProcessRecipe { - private readonly CodexPortMap portMap; - private readonly NumberSource numberSource; + private readonly CodexProcessConfig pc; - public CodexProcessRecipe(CodexPortMap portMap, NumberSource numberSource) + public CodexProcessRecipe(CodexProcessConfig pc) { - this.portMap = portMap; - this.numberSource = numberSource; + this.pc = pc; } public ProcessRecipe Initialize(CodexStartupConfig config) { args.Clear(); - AddArg("--api-port", portMap.ApiPort); + AddArg("--api-port", pc.ApiPort); AddArg("--api-bindaddr", "0.0.0.0"); - var dataDir = $"datadir_{numberSource.GetNextNumber()}"; - AddArg("--data-dir", dataDir); + AddArg("--data-dir", pc.DataDir); - AddArg("--disc-port", portMap.DiscPort); + AddArg("--disc-port", pc.DiscPort); AddArg("--log-level", config.LogLevelWithTopics()); // This makes the node announce itself to its local IP address. @@ -62,7 +63,7 @@ namespace CodexPlugin AddArg("--nat", $"extip:{ipaddrs.ToStringInvariant()}"); - AddArg("--listen-addrs", $"/ip4/0.0.0.0/tcp/{portMap.ListenPort}"); + AddArg("--listen-addrs", $"/ip4/0.0.0.0/tcp/{pc.ListenPort}"); if (!string.IsNullOrEmpty(config.BootstrapSpr)) { diff --git a/ProjectPlugins/CodexPlugin/ContainerCodexStarter.cs b/ProjectPlugins/CodexPlugin/ContainerCodexStarter.cs index 658641b6..0627bc1f 100644 --- a/ProjectPlugins/CodexPlugin/ContainerCodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/ContainerCodexStarter.cs @@ -43,6 +43,10 @@ namespace CodexPlugin return containers.Select(CreateInstance).ToArray(); } + public void Decommission() + { + } + private StartupConfig CreateStartupConfig(CodexSetup codexSetup) { var startupConfig = new StartupConfig(); diff --git a/ProjectPlugins/CodexPlugin/ICodexStarter.cs b/ProjectPlugins/CodexPlugin/ICodexStarter.cs index 4f264342..c88cd382 100644 --- a/ProjectPlugins/CodexPlugin/ICodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/ICodexStarter.cs @@ -5,5 +5,6 @@ namespace CodexPlugin public interface ICodexStarter { ICodexInstance[] BringOnline(CodexSetup codexSetup); + void Decommission(); } } diff --git a/ProjectPlugins/CodexPlugin/ProcessControlMap.cs b/ProjectPlugins/CodexPlugin/ProcessControlMap.cs index a91cce99..99498f57 100644 --- a/ProjectPlugins/CodexPlugin/ProcessControlMap.cs +++ b/ProjectPlugins/CodexPlugin/ProcessControlMap.cs @@ -25,5 +25,13 @@ namespace CodexPlugin { return processControlMap[instance.Name]; } + + public void StopAll() + { + var pcs = processControlMap.Values.ToArray(); + processControlMap.Clear(); + + foreach (var c in pcs) c.Stop(waitTillStopped: true); + } } } From 50e58dc7995b1f9b6d31fd05a12ef28a7d9bbb58 Mon Sep 17 00:00:00 2001 From: ThatBen Date: Tue, 28 Jan 2025 14:58:37 +0100 Subject: [PATCH 19/25] cleanup on startup --- .../CodexPlugin/BinaryCodexStarter.cs | 70 ++++++++++++++++--- .../CodexPlugin/BinaryProcessControl.cs | 1 + ProjectPlugins/CodexPlugin/CodexExePath.cs | 18 +++++ .../CodexPlugin/CodexProcessRecipe.cs | 24 +++---- 4 files changed, 90 insertions(+), 23 deletions(-) create mode 100644 ProjectPlugins/CodexPlugin/CodexExePath.cs diff --git a/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs b/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs index 64eb67b2..7f3c9d00 100644 --- a/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs @@ -9,8 +9,17 @@ namespace CodexPlugin { private readonly IPluginTools pluginTools; private readonly ProcessControlMap processControlMap; - private readonly NumberSource numberSource = new NumberSource(1); - private readonly FreePortFinder freePortFinder = new FreePortFinder(); + private readonly static NumberSource numberSource = new NumberSource(1); + private readonly static FreePortFinder freePortFinder = new FreePortFinder(); + private readonly static object _lock = new object(); + private readonly static string dataParentDir = "codex_disttest_datadirs"; + private readonly static CodexExePath codexExePath = new CodexExePath(); + + static BinaryCodexStarter() + { + StopAllCodexProcesses(); + DeleteParentDataDir(); + } public BinaryCodexStarter(IPluginTools pluginTools, ProcessControlMap processControlMap) { @@ -20,15 +29,21 @@ namespace CodexPlugin public ICodexInstance[] BringOnline(CodexSetup codexSetup) { - LogSeparator(); - Log($"Starting {codexSetup.Describe()}..."); + lock (_lock) + { + LogSeparator(); + Log($"Starting {codexSetup.Describe()}..."); - return StartCodexBinaries(codexSetup, codexSetup.NumberOfNodes); + return StartCodexBinaries(codexSetup, codexSetup.NumberOfNodes); + } } public void Decommission() { - processControlMap.StopAll(); + lock (_lock) + { + processControlMap.StopAll(); + } } private ICodexInstance[] StartCodexBinaries(CodexStartupConfig startupConfig, int numberOfNodes) @@ -45,9 +60,11 @@ namespace CodexPlugin private ICodexInstance StartBinary(CodexStartupConfig config) { var name = "codex_" + numberSource.GetNextNumber(); - var dataDir = $"datadir_{numberSource.GetNextNumber()}"; + var dataDir = Path.Combine(dataParentDir, $"datadir_{numberSource.GetNextNumber()}"); var pconfig = new CodexProcessConfig(name, freePortFinder, dataDir); - var factory = new CodexProcessRecipe(pconfig); + Log(pconfig); + + var factory = new CodexProcessRecipe(pconfig, codexExePath); var recipe = factory.Initialize(config); var startInfo = new ProcessStartInfo( @@ -69,7 +86,7 @@ namespace CodexPlugin name: name, imageName: "binary", startUtc: DateTime.UtcNow, - discoveryEndpoint: new Address("Disc", local, pconfig.DiscPort), + discoveryEndpoint: new Address("Disc", pconfig.LocalIpAddrs.ToString(), pconfig.DiscPort), apiEndpoint: new Address("Api", "http://" + local, pconfig.ApiPort), listenEndpoint: new Address("Listen", local, pconfig.ListenPort), ethAccount: null, @@ -87,9 +104,44 @@ namespace CodexPlugin Log("----------------------------------------------------------------------------"); } + private void Log(CodexProcessConfig pconfig) + { + Log( + "NodeConfig:Name=" + pconfig.Name + + "ApiPort=" + pconfig.ApiPort + + "DiscPort=" + pconfig.DiscPort + + "ListenPort=" + pconfig.ListenPort + + "DataDir=" + pconfig.DataDir + ); + } + private void Log(string message) { pluginTools.GetLog().Log(message); } + + private static void DeleteParentDataDir() + { + if (Directory.Exists(dataParentDir)) + { + Directory.Delete(dataParentDir, true); + } + } + + private static void StopAllCodexProcesses() + { + var processes = Process.GetProcesses(); + var codexes = processes.Where(p => + p.ProcessName.ToLowerInvariant() == "codex" && + p.MainModule != null && + p.MainModule.FileName == codexExePath.Get() + ).ToArray(); + + foreach (var c in codexes) + { + c.Kill(); + c.WaitForExit(); + } + } } } diff --git a/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs b/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs index ce1fbec3..44c843d7 100644 --- a/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs +++ b/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs @@ -54,6 +54,7 @@ namespace CodexPlugin if (waitTillStopped) { + process.WaitForExit(); foreach (var t in streamTasks) t.Wait(); } diff --git a/ProjectPlugins/CodexPlugin/CodexExePath.cs b/ProjectPlugins/CodexPlugin/CodexExePath.cs new file mode 100644 index 00000000..2e4dc557 --- /dev/null +++ b/ProjectPlugins/CodexPlugin/CodexExePath.cs @@ -0,0 +1,18 @@ +namespace CodexPlugin +{ + public class CodexExePath + { + private readonly string path = Path.Combine( + "d:", + "Dev", + "nim-codex", + "build", + "codex.exe" + ); + + public string Get() + { + return path; + } + } +} diff --git a/ProjectPlugins/CodexPlugin/CodexProcessRecipe.cs b/ProjectPlugins/CodexPlugin/CodexProcessRecipe.cs index 98bb9dbf..dbbc497a 100644 --- a/ProjectPlugins/CodexPlugin/CodexProcessRecipe.cs +++ b/ProjectPlugins/CodexPlugin/CodexProcessRecipe.cs @@ -1,6 +1,5 @@ using System.Net.Sockets; using System.Net; -using Utils; using Nethereum.Util; namespace CodexPlugin @@ -26,6 +25,10 @@ namespace CodexPlugin ListenPort = freePortFinder.GetNextFreePort(); Name = name; DataDir = dataDir; + var host = Dns.GetHostEntry(Dns.GetHostName()); + var addrs = host.AddressList.Where(a => a.AddressFamily == AddressFamily.InterNetwork).ToList(); + + LocalIpAddrs = addrs.First(); } public int ApiPort { get; } @@ -33,15 +36,18 @@ namespace CodexPlugin public int ListenPort { get; } public string Name { get; } public string DataDir { get; } + public IPAddress LocalIpAddrs { get; } } public class CodexProcessRecipe { private readonly CodexProcessConfig pc; + private readonly CodexExePath codexExePath; - public CodexProcessRecipe(CodexProcessConfig pc) + public CodexProcessRecipe(CodexProcessConfig pc, CodexExePath codexExePath) { this.pc = pc; + this.codexExePath = codexExePath; } public ProcessRecipe Initialize(CodexStartupConfig config) @@ -57,11 +63,7 @@ namespace CodexPlugin AddArg("--log-level", config.LogLevelWithTopics()); // This makes the node announce itself to its local IP address. - var host = Dns.GetHostEntry(Dns.GetHostName()); - var addrs = host.AddressList.Where(a => a.AddressFamily == AddressFamily.InterNetwork).ToList(); - var ipaddrs = addrs.First(); - - AddArg("--nat", $"extip:{ipaddrs.ToStringInvariant()}"); + AddArg("--nat", $"extip:{pc.LocalIpAddrs.ToStringInvariant()}"); AddArg("--listen-addrs", $"/ip4/0.0.0.0/tcp/{pc.ListenPort}"); @@ -139,13 +141,7 @@ namespace CodexPlugin private ProcessRecipe Create() { return new ProcessRecipe( - cmd: Path.Combine( - "d:", - "Dev", - "nim-codex", - "build", - "codex.exe" - ), + cmd: codexExePath.Get(), args: args.ToArray()); } From e24d5e02f2f34f6d0e7c6accf1e6100e8a6ab23c Mon Sep 17 00:00:00 2001 From: ThatBen Date: Tue, 28 Jan 2025 15:29:25 +0100 Subject: [PATCH 20/25] Fixes build for continuous tests --- Tests/CodexContinuousTests/SingleTestRun.cs | 29 ++++++++++----------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/Tests/CodexContinuousTests/SingleTestRun.cs b/Tests/CodexContinuousTests/SingleTestRun.cs index 643f472e..0606c8e4 100644 --- a/Tests/CodexContinuousTests/SingleTestRun.cs +++ b/Tests/CodexContinuousTests/SingleTestRun.cs @@ -292,25 +292,24 @@ namespace ContinuousTests private ICodexNode[] CreateRandomNodes() { - var containers = SelectRandomContainers(); - fixtureLog.Log("Selected nodes: " + string.Join(",", containers.Select(c => c.Name))); - return entryPoint.CreateInterface().WrapCodexContainers(containers).ToArray(); + var instances = SelectRandomInstance(); + fixtureLog.Log("Selected nodes: " + string.Join(",", instances.Select(c => c.Name))); + return entryPoint.CreateInterface().WrapCodexContainers(instances).ToArray(); } - private RunningPod[] SelectRandomContainers() + private ICodexInstance[] SelectRandomInstance() { - throw new NotImplementedException(); - //var number = handle.Test.RequiredNumberOfNodes; - //var containers = config.CodexDeployment.CodexInstances.Select(i => i.Pod).ToList(); - //if (number == -1) return containers.ToArray(); + var number = handle.Test.RequiredNumberOfNodes; + var instances = config.CodexDeployment.CodexInstances.ToList(); + if (number == -1) return instances.ToArray(); - //var result = new RunningPod[number]; - //for (var i = 0; i < number; i++) - //{ - // result[i] = containers.PickOneRandom(); - //} + var result = new ICodexInstance[number]; + for (var i = 0; i < number; i++) + { + result[i] = instances.PickOneRandom(); + } - //return result; + return result; } } -} \ No newline at end of file +} From 0784804fa4ac55dca4734e35e42a6442f169470e Mon Sep 17 00:00:00 2001 From: ThatBen Date: Wed, 29 Jan 2025 14:44:19 +0100 Subject: [PATCH 21/25] workaround for downloading codex logs --- ProjectPlugins/CodexClient/CodexAccess.cs | 4 +- ProjectPlugins/CodexClient/CodexNode.cs | 6 +- .../CodexClient/Hooks/CodexHooksFactory.cs | 9 +- .../CodexClient/Hooks/CodexNodeHooks.cs | 62 ++++++++++++- .../CodexPlugin/BinaryProcessControl.cs | 2 +- ProjectPlugins/CodexPlugin/CodexPlugin.cs | 5 +- .../CodexPlugin/CoreInterfaceExtensions.cs | 4 +- .../CodexNodeTranscriptWriter.cs | 2 +- Tests/DistTestCore/TestLifecycle.cs | 3 + Tests/ExperimentalTests/CodexDistTest.cs | 86 ++++++++++++++++++- .../DatalayerReliabilityTests.cs | 2 +- .../DetectBlockRetransmitTest.cs | 5 +- .../FullyConnectedDownloadTests.cs | 3 +- .../MultiswarmTests.cs | 7 +- 14 files changed, 178 insertions(+), 22 deletions(-) diff --git a/ProjectPlugins/CodexClient/CodexAccess.cs b/ProjectPlugins/CodexClient/CodexAccess.cs index 7f35d233..9f0c22c7 100644 --- a/ProjectPlugins/CodexClient/CodexAccess.cs +++ b/ProjectPlugins/CodexClient/CodexAccess.cs @@ -31,7 +31,9 @@ namespace CodexClient public IDownloadedLog DownloadLog(string additionalName = "") { - return processControl.DownloadLog(log.CreateSubfile(GetName() + additionalName)); + var file = log.CreateSubfile(GetName() + additionalName); + Log($"Downloading logs for '{GetName()}' to '{file.Filename}'"); + return processControl.DownloadLog(file); } public string GetImageName() diff --git a/ProjectPlugins/CodexClient/CodexNode.cs b/ProjectPlugins/CodexClient/CodexNode.cs index 5cdf7b02..df18079c 100644 --- a/ProjectPlugins/CodexClient/CodexNode.cs +++ b/ProjectPlugins/CodexClient/CodexNode.cs @@ -79,7 +79,7 @@ namespace CodexClient InitializePeerNodeId(); InitializeLogReplacements(); - hooks.OnNodeStarted(peerId, nodeId); + hooks.OnNodeStarted(this, peerId, nodeId); } public IMarketplaceAccess Marketplace { get; } @@ -350,9 +350,9 @@ namespace CodexClient cts.Cancel(); throw new TimeoutException($"Download of '{contentId}' timed out after {Time.FormatDuration(timeout)}"); } - catch + catch (Exception ex) { - Log($"Failed to download file '{contentId}'."); + Log($"Failed to download file '{contentId}': {ex}"); throw; } } diff --git a/ProjectPlugins/CodexClient/Hooks/CodexHooksFactory.cs b/ProjectPlugins/CodexClient/Hooks/CodexHooksFactory.cs index 83e45926..0557f138 100644 --- a/ProjectPlugins/CodexClient/Hooks/CodexHooksFactory.cs +++ b/ProjectPlugins/CodexClient/Hooks/CodexHooksFactory.cs @@ -9,11 +9,14 @@ namespace CodexClient.Hooks public class CodexHooksFactory { - public ICodexHooksProvider Provider { get; set; } = new DoNothingHooksProvider(); + public List Providers { get; } = new List(); public ICodexNodeHooks CreateHooks(string nodeName) { - return Provider.CreateHooks(nodeName); + if (Providers.Count == 0) return new DoNothingCodexHooks(); + + var hooks = Providers.Select(p => p.CreateHooks(nodeName)).ToArray(); + return new MuxingCodexNodeHooks(hooks); } } @@ -43,7 +46,7 @@ namespace CodexClient.Hooks { } - public void OnNodeStarted(string peerId, string nodeId) + public void OnNodeStarted(ICodexNode node, string peerId, string nodeId) { } diff --git a/ProjectPlugins/CodexClient/Hooks/CodexNodeHooks.cs b/ProjectPlugins/CodexClient/Hooks/CodexNodeHooks.cs index 8becef0a..926a5d1d 100644 --- a/ProjectPlugins/CodexClient/Hooks/CodexNodeHooks.cs +++ b/ProjectPlugins/CodexClient/Hooks/CodexNodeHooks.cs @@ -5,7 +5,7 @@ namespace CodexClient.Hooks public interface ICodexNodeHooks { void OnNodeStarting(DateTime startUtc, string image, EthAccount? ethAccount); - void OnNodeStarted(string peerId, string nodeId); + void OnNodeStarted(ICodexNode node, string peerId, string nodeId); void OnNodeStopping(); void OnFileUploading(string uid, ByteSize size); void OnFileUploaded(string uid, ByteSize size, ContentId cid); @@ -15,4 +15,64 @@ namespace CodexClient.Hooks void OnStorageContractUpdated(StoragePurchase purchaseStatus); void OnStorageAvailabilityCreated(StorageAvailability response); } + + public class MuxingCodexNodeHooks : ICodexNodeHooks + { + private readonly ICodexNodeHooks[] backingHooks; + + public MuxingCodexNodeHooks(ICodexNodeHooks[] backingHooks) + { + this.backingHooks = backingHooks; + } + + public void OnFileDownloaded(ByteSize size, ContentId cid) + { + foreach (var h in backingHooks) h.OnFileDownloaded(size, cid); + } + + public void OnFileDownloading(ContentId cid) + { + foreach (var h in backingHooks) h.OnFileDownloading(cid); + } + + public void OnFileUploaded(string uid, ByteSize size, ContentId cid) + { + foreach (var h in backingHooks) h.OnFileUploaded(uid, size, cid); + } + + public void OnFileUploading(string uid, ByteSize size) + { + foreach (var h in backingHooks) h.OnFileUploading(uid, size); + } + + public void OnNodeStarted(ICodexNode node, string peerId, string nodeId) + { + foreach (var h in backingHooks) h.OnNodeStarted(node, peerId, nodeId); + } + + public void OnNodeStarting(DateTime startUtc, string image, EthAccount? ethAccount) + { + foreach (var h in backingHooks) h.OnNodeStarting(startUtc, image, ethAccount); + } + + public void OnNodeStopping() + { + foreach (var h in backingHooks) h.OnNodeStopping(); + } + + public void OnStorageAvailabilityCreated(StorageAvailability response) + { + foreach (var h in backingHooks) h.OnStorageAvailabilityCreated(response); + } + + public void OnStorageContractSubmitted(StoragePurchaseContract storagePurchaseContract) + { + foreach (var h in backingHooks) h.OnStorageContractSubmitted(storagePurchaseContract); + } + + public void OnStorageContractUpdated(StoragePurchase purchaseStatus) + { + foreach (var h in backingHooks) h.OnStorageContractUpdated(purchaseStatus); + } + } } diff --git a/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs b/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs index 44c843d7..63128977 100644 --- a/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs +++ b/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs @@ -44,7 +44,7 @@ namespace CodexPlugin public bool HasCrashed() { - return false; + return process.HasExited; } public void Stop(bool waitTillStopped) diff --git a/ProjectPlugins/CodexPlugin/CodexPlugin.cs b/ProjectPlugins/CodexPlugin/CodexPlugin.cs index b208347c..a68be9e6 100644 --- a/ProjectPlugins/CodexPlugin/CodexPlugin.cs +++ b/ProjectPlugins/CodexPlugin/CodexPlugin.cs @@ -68,9 +68,10 @@ namespace CodexPlugin } } - public void SetCodexHooksProvider(ICodexHooksProvider hooksProvider) + public void AddCodexHooksProvider(ICodexHooksProvider hooksProvider) { - hooksFactory.Provider = hooksProvider; + if (hooksFactory.Providers.Contains(hooksProvider)) return; + hooksFactory.Providers.Add(hooksProvider); } private CodexSetup GetSetup(int numberOfNodes, Action setup) diff --git a/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs b/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs index 06b9246e..383ac38b 100644 --- a/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs +++ b/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs @@ -39,9 +39,9 @@ namespace CodexPlugin return ci.StartCodexNodes(number, s => { }); } - public static void SetCodexHooksProvider(this CoreInterface ci, ICodexHooksProvider hooksProvider) + public static void AddCodexHooksProvider(this CoreInterface ci, ICodexHooksProvider hooksProvider) { - Plugin(ci).SetCodexHooksProvider(hooksProvider); + Plugin(ci).AddCodexHooksProvider(hooksProvider); } private static CodexPlugin Plugin(CoreInterface ci) diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexNodeTranscriptWriter.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexNodeTranscriptWriter.cs index 346715ca..db4702d7 100644 --- a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexNodeTranscriptWriter.cs +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexNodeTranscriptWriter.cs @@ -32,7 +32,7 @@ namespace CodexPlugin.OverwatchSupport }); } - public void OnNodeStarted(string peerId, string nodeId) + public void OnNodeStarted(ICodexNode node, string peerId, string nodeId) { if (string.IsNullOrEmpty(peerId) || string.IsNullOrEmpty(nodeId)) { diff --git a/Tests/DistTestCore/TestLifecycle.cs b/Tests/DistTestCore/TestLifecycle.cs index 2a11cedb..3d642d20 100644 --- a/Tests/DistTestCore/TestLifecycle.cs +++ b/Tests/DistTestCore/TestLifecycle.cs @@ -115,6 +115,9 @@ namespace DistTestCore { try { + // TODO: This code is built on k8s containers. + // It should be remapped to use the project plugin's support for downloading logs (via IProcessControl). + // For now, leave this. Add support for Codex non-container logs using the codex node hooks. var result = new List(); result.AddRange(stoppedContainerLogs); foreach (var rc in runningContainers) diff --git a/Tests/ExperimentalTests/CodexDistTest.cs b/Tests/ExperimentalTests/CodexDistTest.cs index 3d83021a..99671edb 100644 --- a/Tests/ExperimentalTests/CodexDistTest.cs +++ b/Tests/ExperimentalTests/CodexDistTest.cs @@ -1,5 +1,6 @@ using BlockchainUtils; using CodexClient; +using CodexClient.Hooks; using CodexContractsPlugin; using CodexNetDeployer; using CodexPlugin; @@ -16,14 +17,85 @@ using Newtonsoft.Json; using NUnit.Framework; using NUnit.Framework.Constraints; using OverwatchTranscript; +using Utils; namespace CodexTests { + public class CodexLogTrackerProvider : ICodexHooksProvider + { + private readonly Action addNode; + + public CodexLogTrackerProvider(Action addNode) + { + this.addNode = addNode; + } + + // See TestLifecycle.cs DownloadAllLogs() + public ICodexNodeHooks CreateHooks(string nodeName) + { + return new CodexLogTracker(addNode); + } + + public class CodexLogTracker : ICodexNodeHooks + { + private readonly Action addNode; + + public CodexLogTracker(Action addNode) + { + this.addNode = addNode; + } + + public void OnFileDownloaded(ByteSize size, ContentId cid) + { + } + + public void OnFileDownloading(ContentId cid) + { + } + + public void OnFileUploaded(string uid, ByteSize size, ContentId cid) + { + } + + public void OnFileUploading(string uid, ByteSize size) + { + } + + public void OnNodeStarted(ICodexNode node, string peerId, string nodeId) + { + addNode(node); + } + + public void OnNodeStarting(DateTime startUtc, string image, EthAccount? ethAccount) + { + } + + public void OnNodeStopping() + { + } + + public void OnStorageAvailabilityCreated(StorageAvailability response) + { + } + + public void OnStorageContractSubmitted(StoragePurchaseContract storagePurchaseContract) + { + } + + public void OnStorageContractUpdated(StoragePurchase purchaseStatus) + { + } + } + } + public class CodexDistTest : DistTest { private static readonly Dictionary writers = new Dictionary(); private static readonly Dictionary blockCaches = new Dictionary(); + // this entire structure is not good and needs to be destroyed at the earliest convenience: + private static readonly Dictionary> nodes = new Dictionary>(); + public CodexDistTest() { ProjectPlugin.Load(); @@ -43,12 +115,24 @@ namespace CodexTests { base.LifecycleStart(lifecycle); SetupTranscript(lifecycle); + + Ci.AddCodexHooksProvider(new CodexLogTrackerProvider(n => + { + if (!nodes.ContainsKey(lifecycle)) nodes.Add(lifecycle, new List()); + nodes[lifecycle].Add(n); + })); } protected override void LifecycleStop(TestLifecycle lifecycle, DistTestResult result) { base.LifecycleStop(lifecycle, result); TeardownTranscript(lifecycle, result); + + if (!result.Success) + { + var codexNodes = nodes[lifecycle]; + foreach (var node in codexNodes) node.DownloadLog(); + } } public ICodexNode StartCodex() @@ -178,7 +262,7 @@ namespace CodexTests var log = new LogPrefixer(lifecycle.Log, "(Transcript) "); var writer = new CodexTranscriptWriter(log, config, Transcript.NewWriter(log)); - Ci.SetCodexHooksProvider(writer); + Ci.AddCodexHooksProvider(writer); writers.Add(lifecycle, writer); } diff --git a/Tests/ExperimentalTests/DownloadConnectivityTests/DatalayerReliabilityTests.cs b/Tests/ExperimentalTests/DownloadConnectivityTests/DatalayerReliabilityTests.cs index 6c619969..7ffc2d81 100644 --- a/Tests/ExperimentalTests/DownloadConnectivityTests/DatalayerReliabilityTests.cs +++ b/Tests/ExperimentalTests/DownloadConnectivityTests/DatalayerReliabilityTests.cs @@ -1,4 +1,4 @@ -using CodexPlugin; +using CodexClient; using CodexTests; using NUnit.Framework; using Utils; diff --git a/Tests/ExperimentalTests/DownloadConnectivityTests/DetectBlockRetransmitTest.cs b/Tests/ExperimentalTests/DownloadConnectivityTests/DetectBlockRetransmitTest.cs index 6f007c4e..601d2cfc 100644 --- a/Tests/ExperimentalTests/DownloadConnectivityTests/DetectBlockRetransmitTest.cs +++ b/Tests/ExperimentalTests/DownloadConnectivityTests/DetectBlockRetransmitTest.cs @@ -1,7 +1,8 @@ -using NUnit.Framework; +using CodexTests; +using NUnit.Framework; using Utils; -namespace CodexTests.DownloadConnectivityTests +namespace ExperimentalTests.DownloadConnectivityTests { [TestFixture] public class DetectBlockRetransmitTest : AutoBootstrapDistTest diff --git a/Tests/ExperimentalTests/DownloadConnectivityTests/FullyConnectedDownloadTests.cs b/Tests/ExperimentalTests/DownloadConnectivityTests/FullyConnectedDownloadTests.cs index 8898f49c..ec128154 100644 --- a/Tests/ExperimentalTests/DownloadConnectivityTests/FullyConnectedDownloadTests.cs +++ b/Tests/ExperimentalTests/DownloadConnectivityTests/FullyConnectedDownloadTests.cs @@ -1,9 +1,10 @@ using CodexClient; using CodexContractsPlugin; +using CodexTests; using NUnit.Framework; using Utils; -namespace CodexTests.DownloadConnectivityTests +namespace ExperimentalTests.DownloadConnectivityTests { [TestFixture] public class FullyConnectedDownloadTests : AutoBootstrapDistTest diff --git a/Tests/ExperimentalTests/DownloadConnectivityTests/MultiswarmTests.cs b/Tests/ExperimentalTests/DownloadConnectivityTests/MultiswarmTests.cs index 8b8f9d1e..a206d475 100644 --- a/Tests/ExperimentalTests/DownloadConnectivityTests/MultiswarmTests.cs +++ b/Tests/ExperimentalTests/DownloadConnectivityTests/MultiswarmTests.cs @@ -1,10 +1,11 @@ using CodexClient; +using CodexTests; using FileUtils; using Logging; using NUnit.Framework; using Utils; -namespace CodexTests.DownloadConnectivityTests +namespace ExperimentalTests.DownloadConnectivityTests { [TestFixture] public class MultiswarmTests : AutoBootstrapDistTest @@ -206,7 +207,7 @@ namespace CodexTests.DownloadConnectivityTests var available = NodePlans.Where(n => n.Downloads.Count < maxDownloadsPerNode && !n.Contains(notIn) ).ToArray(); - if (available.Any()) return RandomUtils.GetOneRandom(available); + if (available.Any()) return available.GetOneRandom(); var newNodePlan = new NodePlan(NodePlans.Count); NodePlans.Add(newNodePlan); @@ -218,7 +219,7 @@ namespace CodexTests.DownloadConnectivityTests var available = NodePlans.Where(n => n.Uploads.Count < maxUploadsPerNode && !n.Contains(notIn) ).ToArray(); - if (available.Any()) return RandomUtils.GetOneRandom(available); + if (available.Any()) return available.GetOneRandom(); var newNodePlan = new NodePlan(NodePlans.Count); NodePlans.Add(newNodePlan); From c1e3e8be09b485286025d0798519c9eae6ffd2f4 Mon Sep 17 00:00:00 2001 From: ThatBen Date: Fri, 31 Jan 2025 14:24:55 +0100 Subject: [PATCH 22/25] testing in progress --- ProjectPlugins/CodexClient/CodexAccess.cs | 4 +- .../CodexPlugin/BinaryCodexStarter.cs | 13 ++++++- .../CodexPlugin/BinaryProcessControl.cs | 39 ++++++++++++++++--- .../DatalayerReliabilityTests.cs | 6 +-- 4 files changed, 50 insertions(+), 12 deletions(-) diff --git a/ProjectPlugins/CodexClient/CodexAccess.cs b/ProjectPlugins/CodexClient/CodexAccess.cs index 9f0c22c7..137ebbc3 100644 --- a/ProjectPlugins/CodexClient/CodexAccess.cs +++ b/ProjectPlugins/CodexClient/CodexAccess.cs @@ -32,7 +32,7 @@ namespace CodexClient public IDownloadedLog DownloadLog(string additionalName = "") { var file = log.CreateSubfile(GetName() + additionalName); - Log($"Downloading logs for '{GetName()}' to '{file.Filename}'"); + Log($"Downloading logs to '{file.Filename}'"); return processControl.DownloadLog(file); } @@ -282,7 +282,7 @@ namespace CodexClient private void Log(string msg) { - log.Log($"{GetName()} {msg}"); + log.Log($"({GetName()}) {msg}"); } } diff --git a/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs b/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs index 7f3c9d00..617cb701 100644 --- a/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs @@ -59,7 +59,7 @@ namespace CodexPlugin private ICodexInstance StartBinary(CodexStartupConfig config) { - var name = "codex_" + numberSource.GetNextNumber(); + var name = GetName(config); var dataDir = Path.Combine(dataParentDir, $"datadir_{numberSource.GetNextNumber()}"); var pconfig = new CodexProcessConfig(name, freePortFinder, dataDir); Log(pconfig); @@ -93,12 +93,21 @@ namespace CodexPlugin metricsEndpoint: null ); - var pc = new BinaryProcessControl(process, pconfig); + var pc = new BinaryProcessControl(pluginTools.GetLog(), process, pconfig); processControlMap.Add(instance, pc); return instance; } + private string GetName(CodexStartupConfig config) + { + if (!string.IsNullOrEmpty(config.NameOverride)) + { + return config.NameOverride + "_" + numberSource.GetNextNumber(); + } + return "codex_" + numberSource.GetNextNumber(); + } + private void LogSeparator() { Log("----------------------------------------------------------------------------"); diff --git a/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs b/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs index 63128977..de8a75c2 100644 --- a/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs +++ b/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs @@ -6,19 +6,24 @@ namespace CodexPlugin { public class BinaryProcessControl : IProcessControl { + private readonly LogFile logFile; private readonly Process process; private readonly CodexProcessConfig config; - private readonly List logLines = new List(); + private readonly List logBuffer = new List(); + private readonly object bufferLock = new object(); private readonly List streamTasks = new List(); private bool running; - public BinaryProcessControl(Process process, CodexProcessConfig config) + public BinaryProcessControl(ILog log, Process process, CodexProcessConfig config) { + logFile = log.CreateSubfile(config.Name); + running = true; this.process = process; this.config = config; streamTasks.Add(Task.Run(() => ReadProcessStream(process.StandardOutput))); streamTasks.Add(Task.Run(() => ReadProcessStream(process.StandardError))); + streamTasks.Add(Task.Run(() => WriteLog())); } private void ReadProcessStream(StreamReader reader) @@ -26,7 +31,32 @@ namespace CodexPlugin while (running) { var line = reader.ReadLine(); - if (!string.IsNullOrEmpty(line)) logLines.Add(line); + if (!string.IsNullOrEmpty(line)) + { + lock (bufferLock) + { + logBuffer.Add(line); + } + } + } + } + + private void WriteLog() + { + while (running || logBuffer.Count > 0) + { + if (logBuffer.Count > 0) + { + var lines = Array.Empty(); + lock (bufferLock) + { + lines = logBuffer.ToArray(); + logBuffer.Clear(); + } + + foreach (var l in lines) logFile.WriteRaw(l); + } + else Thread.Sleep(100); } } @@ -38,8 +68,7 @@ namespace CodexPlugin public IDownloadedLog DownloadLog(LogFile file) { - foreach (var line in logLines) file.WriteRaw(line); - return new DownloadedLog(file, config.Name); + return new DownloadedLog(logFile, config.Name); } public bool HasCrashed() diff --git a/Tests/ExperimentalTests/DownloadConnectivityTests/DatalayerReliabilityTests.cs b/Tests/ExperimentalTests/DownloadConnectivityTests/DatalayerReliabilityTests.cs index 7ffc2d81..ea501e64 100644 --- a/Tests/ExperimentalTests/DownloadConnectivityTests/DatalayerReliabilityTests.cs +++ b/Tests/ExperimentalTests/DownloadConnectivityTests/DatalayerReliabilityTests.cs @@ -14,15 +14,15 @@ namespace ExperimentalTests.DownloadConnectivityTests [Test] [Combinatorial] public void SingleSetTest( - [Values(1, 10, 100, 1000)] int fileSizeMb, + [Values(10, 100, 1000)] int fileSizeMb, [Values(5, 10, 20, 30)] int numDownloaders ) { var file = GenerateTestFile(fileSizeMb.MB()); - var uploaders = StartCodex(n => n.WithName("uploader")); + var uploader = StartCodex(n => n.WithName("uploader")); var downloaders = StartCodex(numDownloaders, n => n.WithName("downloader")); - var cid = uploaders.UploadFile(file); + var cid = uploader.UploadFile(file); var downloadTasks = new List(); foreach (var dl in downloaders) From 413a46c7619ecea5f1d1f75a07fde4bb064f84f0 Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 3 Feb 2025 10:08:29 +0100 Subject: [PATCH 23/25] Fixes namespaces for experimental tests --- ProjectPlugins/CodexPlugin/CodexExePath.cs | 27 +++++++++++++------ .../BasicTests/AsyncProfiling.cs | 3 ++- .../BasicTests/ExampleTests.cs | 3 ++- .../BasicTests/MarketplaceTests.cs | 9 ++++--- .../BasicTests/PyramidTests.cs | 5 ++-- .../DatalayerReliabilityTests.cs | 4 +-- .../LayeredDiscoveryTests.cs | 5 ++-- .../PeerDiscoveryTests/PeerDiscoveryTests.cs | 3 ++- .../UtilityTests/ClusterSpeedTests.cs | 2 +- .../UtilityTests/DiscordBotTests.cs | 7 ++--- .../UtilityTests/LogHelperTests.cs | 3 ++- .../UtilityTests/NetworkIsolationTest.cs | 2 +- 12 files changed, 46 insertions(+), 27 deletions(-) diff --git a/ProjectPlugins/CodexPlugin/CodexExePath.cs b/ProjectPlugins/CodexPlugin/CodexExePath.cs index 2e4dc557..79e9d4d8 100644 --- a/ProjectPlugins/CodexPlugin/CodexExePath.cs +++ b/ProjectPlugins/CodexPlugin/CodexExePath.cs @@ -2,17 +2,28 @@ { public class CodexExePath { - private readonly string path = Path.Combine( - "d:", - "Dev", - "nim-codex", - "build", - "codex.exe" - ); + private readonly string[] paths = [ + Path.Combine("d:", "Dev", "nim-codex", "build", "codex.exe"), + Path.Combine("c:", "Projects", "nim-codex", "build", "codex.exe") + ]; + + private string selectedPath = string.Empty; + + public CodexExePath() + { + foreach (var p in paths) + { + if (File.Exists(p)) + { + selectedPath = p; + return; + } + } + } public string Get() { - return path; + return selectedPath; } } } diff --git a/Tests/ExperimentalTests/BasicTests/AsyncProfiling.cs b/Tests/ExperimentalTests/BasicTests/AsyncProfiling.cs index 656ea9a6..c74911fc 100644 --- a/Tests/ExperimentalTests/BasicTests/AsyncProfiling.cs +++ b/Tests/ExperimentalTests/BasicTests/AsyncProfiling.cs @@ -1,8 +1,9 @@ using NUnit.Framework; using MetricsPlugin; using Utils; +using CodexTests; -namespace CodexTests.BasicTests +namespace ExperimentalTests.BasicTests { [TestFixture] public class AsyncProfiling : CodexDistTest diff --git a/Tests/ExperimentalTests/BasicTests/ExampleTests.cs b/Tests/ExperimentalTests/BasicTests/ExampleTests.cs index 716d3fff..b4ad44fa 100644 --- a/Tests/ExperimentalTests/BasicTests/ExampleTests.cs +++ b/Tests/ExperimentalTests/BasicTests/ExampleTests.cs @@ -1,11 +1,12 @@ using CodexClient; using CodexPlugin; +using CodexTests; using DistTestCore; using MetricsPlugin; using NUnit.Framework; using Utils; -namespace CodexTests.BasicTests +namespace ExperimentalTests.BasicTests { [TestFixture] public class ExampleTests : CodexDistTest diff --git a/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs b/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs index 8cc596b8..f8ef52d5 100644 --- a/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs +++ b/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs @@ -2,12 +2,13 @@ using CodexContractsPlugin; using CodexContractsPlugin.Marketplace; using CodexPlugin; +using CodexTests; using FileUtils; using GethPlugin; using NUnit.Framework; using Utils; -namespace CodexTests.BasicTests +namespace ExperimentalTests.BasicTests { [TestFixture] public class MarketplaceTests : AutoBootstrapDistTest @@ -24,14 +25,14 @@ namespace CodexTests.BasicTests var hostInitialBalance = 234.TstWei(); var clientInitialBalance = 100000.TstWei(); var fileSize = new ByteSize( - numBlocks * (64 * 1024) + + numBlocks * 64 * 1024 + plusSizeKb * 1024 + plusSizeBytes ); var geth = StartGethNode(s => s.IsMiner().WithName("disttest-geth")); var contracts = Ci.StartCodexContracts(geth); - + var numberOfHosts = 5; var hosts = StartCodex(numberOfHosts, s => s .WithName("Host") @@ -81,7 +82,7 @@ namespace CodexTests.BasicTests }; var purchaseContract = client.Marketplace.RequestStorage(purchase); - + var contractCid = purchaseContract.ContentId; Assert.That(uploadCid.Id, Is.Not.EqualTo(contractCid.Id)); diff --git a/Tests/ExperimentalTests/BasicTests/PyramidTests.cs b/Tests/ExperimentalTests/BasicTests/PyramidTests.cs index 390a6c0e..95d43e83 100644 --- a/Tests/ExperimentalTests/BasicTests/PyramidTests.cs +++ b/Tests/ExperimentalTests/BasicTests/PyramidTests.cs @@ -1,8 +1,9 @@ using CodexClient; +using CodexTests; using NUnit.Framework; using Utils; -namespace CodexTests.BasicTests +namespace ExperimentalTests.BasicTests { [TestFixture] public class PyramidTests : CodexDistTest @@ -46,7 +47,7 @@ namespace CodexTests.BasicTests var uploadTasks = new List>(); foreach (var node in layer) { - uploadTasks.Add(Task.Run(() => + uploadTasks.Add(Task.Run(() => { var file = GenerateTestFile(size); return node.UploadFile(file); diff --git a/Tests/ExperimentalTests/DownloadConnectivityTests/DatalayerReliabilityTests.cs b/Tests/ExperimentalTests/DownloadConnectivityTests/DatalayerReliabilityTests.cs index ea501e64..d218e6ec 100644 --- a/Tests/ExperimentalTests/DownloadConnectivityTests/DatalayerReliabilityTests.cs +++ b/Tests/ExperimentalTests/DownloadConnectivityTests/DatalayerReliabilityTests.cs @@ -14,8 +14,8 @@ namespace ExperimentalTests.DownloadConnectivityTests [Test] [Combinatorial] public void SingleSetTest( - [Values(10, 100, 1000)] int fileSizeMb, - [Values(5, 10, 20, 30)] int numDownloaders + [Values(1000)] int fileSizeMb, + [Values(10, 20, 30)] int numDownloaders ) { var file = GenerateTestFile(fileSizeMb.MB()); diff --git a/Tests/ExperimentalTests/PeerDiscoveryTests/LayeredDiscoveryTests.cs b/Tests/ExperimentalTests/PeerDiscoveryTests/LayeredDiscoveryTests.cs index dc3d049f..bf2ed0e1 100644 --- a/Tests/ExperimentalTests/PeerDiscoveryTests/LayeredDiscoveryTests.cs +++ b/Tests/ExperimentalTests/PeerDiscoveryTests/LayeredDiscoveryTests.cs @@ -1,7 +1,8 @@ using CodexClient; +using CodexTests; using NUnit.Framework; -namespace CodexTests.PeerDiscoveryTests +namespace ExperimentalTests.PeerDiscoveryTests { [TestFixture] public class LayeredDiscoveryTests : CodexDistTest @@ -36,7 +37,7 @@ namespace CodexTests.PeerDiscoveryTests { var nodes = new List(); var node = StartCodex(); - nodes.Add(node); + nodes.Add(node); for (var i = 1; i < chainLength; i++) { diff --git a/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs b/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs index b0373a32..901504c7 100644 --- a/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs +++ b/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs @@ -2,8 +2,9 @@ using CodexClient; using NUnit.Framework; using Utils; +using CodexTests; -namespace CodexTests.PeerDiscoveryTests +namespace ExperimentalTests.PeerDiscoveryTests { [TestFixture] public class PeerDiscoveryTests : AutoBootstrapDistTest diff --git a/Tests/ExperimentalTests/UtilityTests/ClusterSpeedTests.cs b/Tests/ExperimentalTests/UtilityTests/ClusterSpeedTests.cs index 7800e132..03bdb689 100644 --- a/Tests/ExperimentalTests/UtilityTests/ClusterSpeedTests.cs +++ b/Tests/ExperimentalTests/UtilityTests/ClusterSpeedTests.cs @@ -3,7 +3,7 @@ using Logging; using NUnit.Framework; using Utils; -namespace CodexTests.UtilityTests +namespace ExperimentalTests.UtilityTests { [TestFixture] public class ClusterDiscSpeedTests : DistTest diff --git a/Tests/ExperimentalTests/UtilityTests/DiscordBotTests.cs b/Tests/ExperimentalTests/UtilityTests/DiscordBotTests.cs index 008a7a1f..abebac0c 100644 --- a/Tests/ExperimentalTests/UtilityTests/DiscordBotTests.cs +++ b/Tests/ExperimentalTests/UtilityTests/DiscordBotTests.cs @@ -2,6 +2,7 @@ using CodexContractsPlugin; using CodexDiscordBotPlugin; using CodexPlugin; +using CodexTests; using Core; using DiscordRewards; using DistTestCore; @@ -12,7 +13,7 @@ using Newtonsoft.Json; using NUnit.Framework; using Utils; -namespace CodexTests.UtilityTests +namespace ExperimentalTests.UtilityTests { [TestFixture] public class DiscordBotTests : AutoBootstrapDistTest @@ -25,7 +26,7 @@ namespace CodexTests.UtilityTests private readonly List rewardsSeen = new List(); private readonly TimeSpan rewarderInterval = TimeSpan.FromMinutes(1); private readonly List receivedEvents = new List(); - + [Test] [DontDownloadLogs] [Ignore("Used to debug testnet bots.")] @@ -238,7 +239,7 @@ namespace CodexTests.UtilityTests if (h > minNumHosts) minNumHosts = h; } - var minFileSize = ((minSlotSize + 1024) * minNumHosts); + var minFileSize = (minSlotSize + 1024) * minNumHosts; return new ByteSize(Convert.ToInt64(minFileSize)); } diff --git a/Tests/ExperimentalTests/UtilityTests/LogHelperTests.cs b/Tests/ExperimentalTests/UtilityTests/LogHelperTests.cs index d6a1dfc4..06f220e4 100644 --- a/Tests/ExperimentalTests/UtilityTests/LogHelperTests.cs +++ b/Tests/ExperimentalTests/UtilityTests/LogHelperTests.cs @@ -1,8 +1,9 @@ using CodexClient; +using CodexTests; using NUnit.Framework; using Utils; -namespace CodexTests.UtilityTests +namespace ExperimentalTests.UtilityTests { [TestFixture] public class LogHelperTests : AutoBootstrapDistTest diff --git a/Tests/ExperimentalTests/UtilityTests/NetworkIsolationTest.cs b/Tests/ExperimentalTests/UtilityTests/NetworkIsolationTest.cs index a86f6260..ea16dbb4 100644 --- a/Tests/ExperimentalTests/UtilityTests/NetworkIsolationTest.cs +++ b/Tests/ExperimentalTests/UtilityTests/NetworkIsolationTest.cs @@ -4,7 +4,7 @@ using DistTestCore; using NUnit.Framework; using Utils; -namespace CodexTests.UtilityTests +namespace ExperimentalTests.UtilityTests { // Warning! // This is a test to check network-isolation in the test-infrastructure. From 1e035d6a685b3463f6dc579320aeec13151aabd0 Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 3 Feb 2025 10:47:37 +0100 Subject: [PATCH 24/25] Faster binary-process log writing --- Framework/Logging/LogFile.cs | 15 +++++++++++++++ .../CodexPlugin/BinaryProcessControl.cs | 10 +++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Framework/Logging/LogFile.cs b/Framework/Logging/LogFile.cs index f9147ce9..2ec94be0 100644 --- a/Framework/Logging/LogFile.cs +++ b/Framework/Logging/LogFile.cs @@ -35,6 +35,21 @@ namespace Logging } } + public void WriteRawMany(IEnumerable lines) + { + try + { + lock (fileLock) + { + File.AppendAllLines(Filename, lines); + } + } + catch (Exception ex) + { + Console.WriteLine("Writing to log has failed: " + ex); + } + } + private static string GetTimestamp() { return $"[{Time.FormatTimestamp(DateTime.UtcNow)}]"; diff --git a/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs b/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs index de8a75c2..c4ed3ee1 100644 --- a/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs +++ b/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs @@ -9,7 +9,7 @@ namespace CodexPlugin private readonly LogFile logFile; private readonly Process process; private readonly CodexProcessConfig config; - private readonly List logBuffer = new List(); + private List logBuffer = new List(); private readonly object bufferLock = new object(); private readonly List streamTasks = new List(); private bool running; @@ -47,14 +47,14 @@ namespace CodexPlugin { if (logBuffer.Count > 0) { - var lines = Array.Empty(); + List lines = null!; lock (bufferLock) { - lines = logBuffer.ToArray(); - logBuffer.Clear(); + lines = logBuffer; + logBuffer = new List(); } - foreach (var l in lines) logFile.WriteRaw(l); + logFile.WriteRawMany(lines); } else Thread.Sleep(100); } From 3459547f208991696062300c982e0c12bc1499fa Mon Sep 17 00:00:00 2001 From: ThatBen Date: Fri, 21 Feb 2025 13:57:25 +0100 Subject: [PATCH 25/25] update --- ProjectPlugins/CodexClient/openapi.yaml | 6 ------ ProjectPlugins/CodexPlugin/ApiChecker.cs | 8 ++++---- ProjectPlugins/CodexPlugin/CodexPlugin.cs | 20 +++++++++++++++++--- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/ProjectPlugins/CodexClient/openapi.yaml b/ProjectPlugins/CodexClient/openapi.yaml index 8e918cc2..6a424985 100644 --- a/ProjectPlugins/CodexClient/openapi.yaml +++ b/ProjectPlugins/CodexClient/openapi.yaml @@ -371,12 +371,6 @@ components: nullable: true description: "The original mimetype of the uploaded content (optional)" example: image/png - uploadedAt: - type: integer - format: int64 - nullable: true - description: "The UTC upload timestamp in seconds" - example: 1729244192 Space: type: object diff --git a/ProjectPlugins/CodexPlugin/ApiChecker.cs b/ProjectPlugins/CodexPlugin/ApiChecker.cs index 318f3185..778977d3 100644 --- a/ProjectPlugins/CodexPlugin/ApiChecker.cs +++ b/ProjectPlugins/CodexPlugin/ApiChecker.cs @@ -10,7 +10,7 @@ namespace CodexPlugin public class ApiChecker { // - private const string OpenApiYamlHash = "6E-0D-3F-26-51-3B-C0-16-1A-A4-81-86-80-CA-08-BC-CB-6C-8A-2C-49-4B-30-CB-75-D8-0F-EA-9D-57-D6-8A"; + private const string OpenApiYamlHash = "69-7C-8A-AC-87-6E-E2-D1-C4-C2-7B-C7-79-6F-15-03-38-5B-18-3D-40-7D-33-A6-62-7B-33-55-0A-4D-64-8B"; private const string OpenApiFilePath = "/codex/openapi.yaml"; private const string DisableEnvironmentVariable = "CODEXPLUGIN_DISABLE_APICHECK"; @@ -82,12 +82,12 @@ namespace CodexPlugin private void OverwriteOpenApiYaml(string containerApi) { Log("API compatibility check failed. Updating CodexPlugin..."); - var openApiFilePath = Path.Combine(PluginPathUtils.ProjectPluginsDir, "CodexPlugin", "openapi.yaml"); - if (!File.Exists(openApiFilePath)) throw new Exception("Unable to locate CodexPlugin/openapi.yaml. Expected: " + openApiFilePath); + var openApiFilePath = Path.Combine(PluginPathUtils.ProjectPluginsDir, "CodexClient", "openapi.yaml"); + if (!File.Exists(openApiFilePath)) throw new Exception("Unable to locate CodexClient/openapi.yaml. Expected: " + openApiFilePath); File.Delete(openApiFilePath); File.WriteAllText(openApiFilePath, containerApi); - Log("CodexPlugin/openapi.yaml has been updated."); + Log("CodexClient/openapi.yaml has been updated."); } private string Hash(string file) diff --git a/ProjectPlugins/CodexPlugin/CodexPlugin.cs b/ProjectPlugins/CodexPlugin/CodexPlugin.cs index a68be9e6..02fbeaa6 100644 --- a/ProjectPlugins/CodexPlugin/CodexPlugin.cs +++ b/ProjectPlugins/CodexPlugin/CodexPlugin.cs @@ -6,6 +6,8 @@ namespace CodexPlugin { public class CodexPlugin : IProjectPlugin, IHasLogPrefix, IHasMetadata { + private const bool UseContainers = true; + private readonly ICodexStarter codexStarter; private readonly IPluginTools tools; private readonly CodexLogLevel defaultLogLevel = CodexLogLevel.Trace; @@ -15,10 +17,22 @@ namespace CodexPlugin public CodexPlugin(IPluginTools tools) { - //codexStarter = new ContainerCodexStarter(tools, processControlMap); - codexStarter = new BinaryCodexStarter(tools, processControlMap); - codexWrapper = new CodexWrapper(tools, processControlMap, hooksFactory); this.tools = tools; + + codexStarter = CreateCodexStarter(); + codexWrapper = new CodexWrapper(tools, processControlMap, hooksFactory); + } + + private ICodexStarter CreateCodexStarter() + { + if (UseContainers) + { + Log("Using Containerized Codex instances"); + return new ContainerCodexStarter(tools, processControlMap); + } + + Log("Using Binary Codex instances"); + return new BinaryCodexStarter(tools, processControlMap); } public string LogPrefix => "(Codex) ";