From 927ccaa1196bef9a33faf70a5c589b7a6940df9f Mon Sep 17 00:00:00 2001 From: benbierens Date: Fri, 26 Jul 2024 08:39:27 +0200 Subject: [PATCH] successful transcript for two-client test --- ProjectPlugins/CodexPlugin/CodexNode.cs | 32 +++++-- .../CodexPlugin/CodexNodeFactory.cs | 10 +- ProjectPlugins/CodexPlugin/CodexNodeGroup.cs | 1 + ProjectPlugins/CodexPlugin/CodexPlugin.cs | 6 ++ ProjectPlugins/CodexPlugin/CodexPlugin.csproj | 1 + ProjectPlugins/CodexPlugin/CodexStarter.cs | 7 +- .../CodexPlugin/CoreInterfaceExtensions.cs | 8 +- .../CodexPlugin/Hooks/CodexHooksFactory.cs | 44 +++++++++ .../CodexPlugin/Hooks/CodexNodeHooks.cs | 10 ++ .../OverwatchSupport/CodexTranscriptWriter.cs | 91 ++++++++++++++++++- .../OverwatchSupport/ModelExtensions.cs | 1 + Tests/CodexTests/CodexDistTest.cs | 16 ++-- 12 files changed, 206 insertions(+), 21 deletions(-) create mode 100644 ProjectPlugins/CodexPlugin/Hooks/CodexHooksFactory.cs create mode 100644 ProjectPlugins/CodexPlugin/Hooks/CodexNodeHooks.cs diff --git a/ProjectPlugins/CodexPlugin/CodexNode.cs b/ProjectPlugins/CodexPlugin/CodexNode.cs index 47b5961..57f610d 100644 --- a/ProjectPlugins/CodexPlugin/CodexNode.cs +++ b/ProjectPlugins/CodexPlugin/CodexNode.cs @@ -1,4 +1,5 @@ -using Core; +using CodexPlugin.Hooks; +using Core; using FileUtils; using GethPlugin; using KubernetesWorkflow; @@ -12,6 +13,7 @@ namespace CodexPlugin public interface ICodexNode : IHasContainer, IHasMetricsScrapeTarget, IHasEthAddress { string GetName(); + string GetPeerId(); DebugInfo GetDebugInfo(); DebugPeer GetDebugPeer(string peerId); ContentId UploadFile(TrackedFile file); @@ -40,20 +42,28 @@ namespace CodexPlugin { private const string UploadFailedMessage = "Unable to store block"; private readonly IPluginTools tools; + private readonly ICodexNodeHooks hooks; private readonly EthAccount? ethAccount; private readonly TransferSpeeds transferSpeeds; + private string peerId = string.Empty; - public CodexNode(IPluginTools tools, CodexAccess codexAccess, CodexNodeGroup group, IMarketplaceAccess marketplaceAccess, EthAccount? ethAccount) + public CodexNode(IPluginTools tools, CodexAccess codexAccess, CodexNodeGroup group, IMarketplaceAccess marketplaceAccess, ICodexNodeHooks hooks, EthAccount? ethAccount) { this.tools = tools; this.ethAccount = ethAccount; CodexAccess = codexAccess; Group = group; Marketplace = marketplaceAccess; + this.hooks = hooks; Version = new DebugInfoVersion(); transferSpeeds = new TransferSpeeds(); } + public void Initialize() + { + hooks.OnNodeStarted(peerId, Container.Recipe.Image); + } + public RunningPod Pod { get { return CodexAccess.Container; } } public RunningContainer Container { get { return Pod.Containers.Single(); } } @@ -95,6 +105,11 @@ namespace CodexPlugin return Container.Name; } + public string GetPeerId() + { + return peerId; + } + public DebugInfo GetDebugInfo() { var debugInfo = CodexAccess.GetDebugInfo(); @@ -132,7 +147,9 @@ namespace CodexPlugin Log($"Uploaded file. Received contentId: '{response}'."); - return new ContentId(response); + var cid = new ContentId(response); + hooks.OnFileUploaded(cid); + return cid; } public TrackedFile? DownloadContent(ContentId contentId, string fileLabel = "") @@ -148,6 +165,7 @@ namespace CodexPlugin var measurement = Stopwatch.Measure(tools.GetLog(), logMessage, () => DownloadToFile(contentId.Id, file, onFailure)); transferSpeeds.AddDownloadSample(file.GetFilesize(), measurement); Log($"Downloaded file {file.Describe()} to '{file.Filename}'."); + hooks.OnFileDownloaded(contentId); return file; } @@ -185,6 +203,8 @@ namespace CodexPlugin public void Stop(bool waitTillStopped) { Log("Stopping..."); + hooks.OnNodeStopping(); + CrashWatcher.Stop(); Group.Stop(this, waitTillStopped); } @@ -192,7 +212,7 @@ namespace CodexPlugin public void EnsureOnlineGetVersionResponse() { var debugInfo = Time.Retry(CodexAccess.GetDebugInfo, "ensure online"); - var nodePeerId = debugInfo.Id; + peerId = debugInfo.Id; var nodeName = CodexAccess.Container.Name; if (!debugInfo.Version.IsValid()) @@ -201,8 +221,8 @@ namespace CodexPlugin } var log = tools.GetLog(); - log.AddStringReplace(nodePeerId, nodeName); - log.AddStringReplace(ToShortIdString(nodePeerId), nodeName); + log.AddStringReplace(peerId, nodeName); + log.AddStringReplace(ToShortIdString(peerId), nodeName); log.AddStringReplace(debugInfo.Table.LocalNode.NodeId, nodeName); log.AddStringReplace(ToShortIdString(debugInfo.Table.LocalNode.NodeId), nodeName); Version = debugInfo.Version; diff --git a/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs b/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs index 425d489..2da8a55 100644 --- a/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs +++ b/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs @@ -1,4 +1,5 @@ -using Core; +using CodexPlugin.Hooks; +using Core; using GethPlugin; using KubernetesWorkflow; using KubernetesWorkflow.Types; @@ -14,17 +15,20 @@ namespace CodexPlugin public class CodexNodeFactory : ICodexNodeFactory { private readonly IPluginTools tools; + private readonly CodexHooksFactory codexHooksFactory; - public CodexNodeFactory(IPluginTools tools) + public CodexNodeFactory(IPluginTools tools, CodexHooksFactory codexHooksFactory) { this.tools = tools; + this.codexHooksFactory = codexHooksFactory; } public CodexNode CreateOnlineCodexNode(CodexAccess access, CodexNodeGroup group) { var ethAccount = GetEthAccount(access); var marketplaceAccess = GetMarketplaceAccess(access, ethAccount); - return new CodexNode(tools, access, group, marketplaceAccess, ethAccount); + var hooks = codexHooksFactory.CreateHooks(access.Container.Name); + return new CodexNode(tools, access, group, marketplaceAccess, hooks, ethAccount); } private IMarketplaceAccess GetMarketplaceAccess(CodexAccess codexAccess, EthAccount? ethAccount) diff --git a/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs b/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs index 47bf35d..cef0536 100644 --- a/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs +++ b/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs @@ -79,6 +79,7 @@ namespace CodexPlugin } Version = first; + foreach (var node in Nodes) node.Initialize(); } private CodexNode CreateOnlineCodexNode(RunningPod c, IPluginTools tools, ICodexNodeFactory factory) diff --git a/ProjectPlugins/CodexPlugin/CodexPlugin.cs b/ProjectPlugins/CodexPlugin/CodexPlugin.cs index 7b722ed..9dba163 100644 --- a/ProjectPlugins/CodexPlugin/CodexPlugin.cs +++ b/ProjectPlugins/CodexPlugin/CodexPlugin.cs @@ -1,3 +1,4 @@ +using CodexPlugin.Hooks; using Core; using KubernetesWorkflow.Types; @@ -57,6 +58,11 @@ namespace CodexPlugin } } + public void SetCodexHooksProvider(ICodexHooksProvider hooksProvider) + { + codexStarter.HooksFactory.Provider = hooksProvider; + } + private CodexSetup GetSetup(int numberOfNodes, Action setup) { var codexSetup = new CodexSetup(numberOfNodes); diff --git a/ProjectPlugins/CodexPlugin/CodexPlugin.csproj b/ProjectPlugins/CodexPlugin/CodexPlugin.csproj index ffdff42..5ca66a5 100644 --- a/ProjectPlugins/CodexPlugin/CodexPlugin.csproj +++ b/ProjectPlugins/CodexPlugin/CodexPlugin.csproj @@ -29,6 +29,7 @@ + diff --git a/ProjectPlugins/CodexPlugin/CodexStarter.cs b/ProjectPlugins/CodexPlugin/CodexStarter.cs index eee5ef3..59edf4e 100644 --- a/ProjectPlugins/CodexPlugin/CodexStarter.cs +++ b/ProjectPlugins/CodexPlugin/CodexStarter.cs @@ -1,4 +1,5 @@ -using Core; +using CodexPlugin.Hooks; +using Core; using KubernetesWorkflow; using KubernetesWorkflow.Types; using Logging; @@ -19,6 +20,8 @@ namespace CodexPlugin apiChecker = new ApiChecker(pluginTools); } + public CodexHooksFactory HooksFactory { get; } = new CodexHooksFactory(); + public RunningPod[] BringOnline(CodexSetup codexSetup) { LogSeparator(); @@ -43,7 +46,7 @@ namespace CodexPlugin public ICodexNodeGroup WrapCodexContainers(CoreInterface coreInterface, RunningPod[] containers) { - var codexNodeFactory = new CodexNodeFactory(pluginTools); + var codexNodeFactory = new CodexNodeFactory(pluginTools, HooksFactory); var group = CreateCodexGroup(coreInterface, containers, codexNodeFactory); diff --git a/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs b/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs index 5f6065d..6fb8842 100644 --- a/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs +++ b/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs @@ -1,4 +1,5 @@ -using Core; +using CodexPlugin.Hooks; +using Core; using KubernetesWorkflow.Types; namespace CodexPlugin @@ -38,6 +39,11 @@ namespace CodexPlugin return ci.StartCodexNodes(number, s => { }); } + public static void SetCodexHooksProvider(this CoreInterface ci, ICodexHooksProvider hooksProvider) + { + Plugin(ci).SetCodexHooksProvider(hooksProvider); + } + private static CodexPlugin Plugin(CoreInterface ci) { return ci.GetPlugin(); diff --git a/ProjectPlugins/CodexPlugin/Hooks/CodexHooksFactory.cs b/ProjectPlugins/CodexPlugin/Hooks/CodexHooksFactory.cs new file mode 100644 index 0000000..d3c9586 --- /dev/null +++ b/ProjectPlugins/CodexPlugin/Hooks/CodexHooksFactory.cs @@ -0,0 +1,44 @@ +namespace CodexPlugin.Hooks +{ + public interface ICodexHooksProvider + { + ICodexNodeHooks CreateHooks(string nodeName); + } + + public class CodexHooksFactory + { + public ICodexHooksProvider Provider { get; set; } = new DoNothingHooksProvider(); + + public ICodexNodeHooks CreateHooks(string nodeName) + { + return Provider.CreateHooks(nodeName); + } + } + + public class DoNothingHooksProvider : ICodexHooksProvider + { + public ICodexNodeHooks CreateHooks(string nodeName) + { + return new DoNothingCodexHooks(); + } + } + + public class DoNothingCodexHooks : ICodexNodeHooks + { + public void OnFileDownloaded(ContentId cid) + { + } + + public void OnFileUploaded(ContentId cid) + { + } + + public void OnNodeStarted(string name, string image) + { + } + + public void OnNodeStopping() + { + } + } +} diff --git a/ProjectPlugins/CodexPlugin/Hooks/CodexNodeHooks.cs b/ProjectPlugins/CodexPlugin/Hooks/CodexNodeHooks.cs new file mode 100644 index 0000000..4be0ce1 --- /dev/null +++ b/ProjectPlugins/CodexPlugin/Hooks/CodexNodeHooks.cs @@ -0,0 +1,10 @@ +namespace CodexPlugin.Hooks +{ + public interface ICodexNodeHooks + { + void OnNodeStarted(string peerId, string image); + void OnNodeStopping(); + void OnFileUploaded(ContentId cid); + void OnFileDownloaded(ContentId cid); + } +} diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexTranscriptWriter.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexTranscriptWriter.cs index 4927e9b..8430e35 100644 --- a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexTranscriptWriter.cs +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexTranscriptWriter.cs @@ -1,9 +1,10 @@ -using Core; +using CodexPlugin.Hooks; +using Core; using OverwatchTranscript; namespace CodexPlugin.OverwatchSupport { - public class CodexTranscriptWriter + public class CodexTranscriptWriter : ICodexHooksProvider { private readonly ITranscriptWriter transcriptWriter; @@ -12,9 +13,93 @@ namespace CodexPlugin.OverwatchSupport this.transcriptWriter = transcriptWriter; } + public void Finalize(string outputFilepath) + { + transcriptWriter.Write(outputFilepath); + } + + public ICodexNodeHooks CreateHooks(string nodeName) + { + return new CodexNodeTranscriptWriter(transcriptWriter, nodeName); + } + public void ProcessLogs(IDownloadedLog[] downloadedLogs) { - throw new NotImplementedException(); + // which logs to which nodes? + // nodeIDs, peerIDs needed. + } + } + + public class CodexNodeTranscriptWriter : ICodexNodeHooks + { + private readonly ITranscriptWriter writer; + private readonly string name; + private string peerId = string.Empty; + + public CodexNodeTranscriptWriter(ITranscriptWriter writer, string name) + { + this.writer = writer; + this.name = name; + } + + public void OnNodeStarted(string peerId, string image) + { + this.peerId = peerId; + WriteCodexEvent(e => + { + e.NodeStarted = new NodeStartedEvent + { + Name = name, + Image = image, + Args = string.Empty + }; + }); + } + + public void OnNodeStopping() + { + WriteCodexEvent(e => + { + e.NodeStopped = new NodeStoppedEvent + { + Name = name + }; + }); + } + + public void OnFileDownloaded(ContentId cid) + { + WriteCodexEvent(e => + { + e.FileDownloaded = new FileDownloadedEvent + { + Cid = cid.Id + }; + }); + } + + public void OnFileUploaded(ContentId cid) + { + WriteCodexEvent(e => + { + e.FileUploaded = new FileUploadedEvent + { + Cid = cid.Id + }; + }); + } + + private void WriteCodexEvent(Action action) + { + if (string.IsNullOrEmpty(peerId)) throw new Exception("PeerId required"); + + var e = new OverwatchCodexEvent + { + PeerId = peerId + }; + action(e); + + writer.Add(DateTime.UtcNow, e); } } } diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/ModelExtensions.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/ModelExtensions.cs index aae8384..dfed289 100644 --- a/ProjectPlugins/CodexPlugin/OverwatchSupport/ModelExtensions.cs +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/ModelExtensions.cs @@ -9,6 +9,7 @@ [Serializable] public class OverwatchCodexEvent { + public string PeerId { get; set; } = string.Empty; public ScenarioFinishedEvent? ScenarioFinished { get; set; } public NodeStartedEvent? NodeStarted { get; set; } public NodeStoppedEvent? NodeStopped { get; set; } diff --git a/Tests/CodexTests/CodexDistTest.cs b/Tests/CodexTests/CodexDistTest.cs index bd4fcac..8891be6 100644 --- a/Tests/CodexTests/CodexDistTest.cs +++ b/Tests/CodexTests/CodexDistTest.cs @@ -7,6 +7,7 @@ using Core; using DistTestCore; using DistTestCore.Helpers; using DistTestCore.Logs; +using Logging; using MetricsPlugin; using Newtonsoft.Json; using NUnit.Framework.Constraints; @@ -39,7 +40,9 @@ namespace CodexTests base.LifecycleStart(lifecycle); if (!enableOverwatchTranscript) return; - writers.Add(lifecycle, new CodexTranscriptWriter(Transcript.NewWriter())); + var writer = new CodexTranscriptWriter(Transcript.NewWriter()); + Ci.SetCodexHooksProvider(writer); + writers.Add(lifecycle, writer); } protected override void LifecycleStop(TestLifecycle lifecycle) @@ -51,6 +54,12 @@ namespace CodexTests writers.Remove(lifecycle); writer.ProcessLogs(lifecycle.DownloadAllLogs()); + + var file = lifecycle.Log.CreateSubfile("owts"); + Stopwatch.Measure(lifecycle.Log, $"Transcript.Finalize: {file.FullFilename}", () => + { + writer.Finalize(file.FullFilename); + }); } public ICodexNode StartCodex() @@ -74,11 +83,6 @@ namespace CodexTests { setup(s); OnCodexSetup(s); - - if (enableOverwatchTranscript) - { - s.WithTranscriptWriter(writers[Get()]); - } }); return group;