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/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/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/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/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/KubernetesWorkflow/CrashWatcher.cs b/Framework/KubernetesWorkflow/ContainerCrashWatcher.cs
similarity index 92%
rename from Framework/KubernetesWorkflow/CrashWatcher.cs
rename to Framework/KubernetesWorkflow/ContainerCrashWatcher.cs
index 1692d920..2932b01f 100644
--- a/Framework/KubernetesWorkflow/CrashWatcher.cs
+++ b/Framework/KubernetesWorkflow/ContainerCrashWatcher.cs
@@ -3,7 +3,7 @@ using Logging;
namespace KubernetesWorkflow
{
- public class CrashWatcher
+ public class ContainerCrashWatcher
{
private readonly ILog log;
private readonly KubernetesClientConfiguration config;
@@ -15,7 +15,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 +45,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/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/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 5c67d54a..67d01def 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));
}
@@ -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)
@@ -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/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..2ec94be0 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,14 +35,19 @@ namespace Logging
}
}
- public void ConcatToFilename(string toAdd)
+ public void WriteRawMany(IEnumerable lines)
{
- var oldFullName = FullFilename;
-
- filename += toAdd;
- FullFilename = filename + "." + extension;
-
- File.Move(oldFullName, FullFilename);
+ try
+ {
+ lock (fileLock)
+ {
+ File.AppendAllLines(Filename, lines);
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Writing to log has failed: " + ex);
+ }
}
private static string GetTimestamp()
diff --git a/Framework/Utils/Address.cs b/Framework/Utils/Address.cs
index 4d370836..010822b9 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; }
@@ -20,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/Framework/Utils/CrashWatcher.cs b/Framework/Utils/CrashWatcher.cs
new file mode 100644
index 00000000..9477bdf8
--- /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/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 59%
rename from ProjectPlugins/CodexPlugin/CodexAccess.cs
rename to ProjectPlugins/CodexClient/CodexAccess.cs
index 5d593758..137ebbc3 100644
--- a/ProjectPlugins/CodexPlugin/CodexAccess.cs
+++ b/ProjectPlugins/CodexClient/CodexAccess.cs
@@ -1,31 +1,50 @@
using CodexOpenApi;
-using Core;
-using KubernetesWorkflow;
-using KubernetesWorkflow.Types;
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, RunningPod container, CrashWatcher crashWatcher)
+ public CodexAccess(ILog log, IHttpFactory httpFactory, IProcessControl processControl, ICodexInstance instance)
{
- this.tools = tools;
- log = tools.GetLog();
- Container = container;
- CrashWatcher = crashWatcher;
-
- CrashWatcher.Start();
+ this.log = log;
+ this.httpFactory = httpFactory;
+ this.processControl = processControl;
+ this.instance = instance;
}
- public RunningPod Container { get; }
- public CrashWatcher CrashWatcher { get; }
+ public void Stop(bool waitTillStopped)
+ {
+ processControl.Stop(waitTillStopped);
+ // Prevents accidental use after stop:
+ instance = null!;
+ }
+
+ public IDownloadedLog DownloadLog(string additionalName = "")
+ {
+ var file = log.CreateSubfile(GetName() + additionalName);
+ Log($"Downloading logs to '{file.Filename}'");
+ return processControl.DownloadLog(file);
+ }
+
+ public string GetImageName()
+ {
+ return instance.ImageName;
+ }
+
+ public DateTime GetStartUtc()
+ {
+ return instance.StartUtc;
+ }
public DebugInfo GetDebugInfo()
{
@@ -79,19 +98,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;
}
@@ -123,7 +137,6 @@ namespace CodexPlugin
return JsonConvert.DeserializeObject(str)!;
}, nameof(LocalFiles));
}));
-
}
public StorageAvailability SalesAvailability(StorageAvailability request)
@@ -170,47 +183,60 @@ 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;
}
- public void DeleteRepoFolder()
+ public Address GetApiEndpoint()
{
- 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);
- }
+ return instance.ApiEndpoint;
}
- private T OnCodex(Func> action)
+ public Address GetListenEndpoint()
{
- var result = tools.CreateHttp(GetHttpId(), CheckContainerCrashed).OnClient(client => CallCodex(client, action));
+ return instance.ListenEndpoint;
+ }
+
+ public bool HasCrashed()
+ {
+ return processControl.HasCrashed();
+ }
+
+ public Address? GetMetricsEndpoint()
+ {
+ return instance.MetricsEndpoint;
+ }
+
+ public EthAccount? GetEthAccount()
+ {
+ return instance.EthAccount;
+ }
+
+ public void DeleteDataDirFolder()
+ {
+ processControl.DeleteDataDirFolder();
+ }
+
+ 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 = tools.CreateHttp(GetHttpId(), CheckContainerCrashed).OnClient(client => CallCodex(client, action), 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 CodexApi(client);
+ var api = new CodexApiClient(client);
api.BaseUrl = $"{address.Host}:{address.Port}/api/codex/v1";
return CrashCheck(() => Time.Wait(action(api)));
}
@@ -223,20 +249,20 @@ namespace CodexPlugin
}
finally
{
- CrashWatcher.HasContainerCrashed();
+ CheckContainerCrashed();
}
}
private IEndpoint GetEndpoint()
{
- return tools
- .CreateHttp(GetHttpId(), CheckContainerCrashed)
- .CreateEndpoint(GetAddress(), "/api/codex/v1/", Container.Name);
+ return httpFactory
+ .CreateHttp(GetHttpId(), h => CheckContainerCrashed())
+ .CreateEndpoint(GetAddress(), "/api/codex/v1/", GetName());
}
private Address GetAddress()
{
- return Container.Containers.Single().GetAddress(CodexContainerRecipe.ApiPortTag);
+ return instance.ApiEndpoint;
}
private string GetHttpId()
@@ -244,52 +270,9 @@ namespace CodexPlugin
return GetAddress().ToString();
}
- private void CheckContainerCrashed(HttpClient client)
+ private void CheckContainerCrashed()
{
- if (CrashWatcher.HasContainerCrashed()) 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);
- }
+ if (processControl.HasCrashed()) throw new Exception($"Container {GetName()} has crashed.");
}
private void Throw(Failure failure)
@@ -299,7 +282,7 @@ namespace CodexPlugin
private void Log(string msg)
{
- log.Log($"{GetName()} {msg}");
+ log.Log($"({GetName()}) {msg}");
}
}
diff --git a/ProjectPlugins/CodexClient/CodexClient.csproj b/ProjectPlugins/CodexClient/CodexClient.csproj
new file mode 100644
index 00000000..cc417a9e
--- /dev/null
+++ b/ProjectPlugins/CodexClient/CodexClient.csproj
@@ -0,0 +1,35 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+ 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..4f77776b
--- /dev/null
+++ b/ProjectPlugins/CodexClient/CodexInstance.cs
@@ -0,0 +1,54 @@
+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; }
+
+ 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/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 69%
rename from ProjectPlugins/CodexPlugin/CodexNode.cs
rename to ProjectPlugins/CodexClient/CodexNode.cs
index afb78f34..df18079c 100644
--- a/ProjectPlugins/CodexPlugin/CodexNode.cs
+++ b/ProjectPlugins/CodexClient/CodexNode.cs
@@ -1,27 +1,21 @@
-using CodexPlugin.Hooks;
-using Core;
+using CodexClient.Hooks;
using FileUtils;
-using GethPlugin;
-using KubernetesWorkflow;
-using KubernetesWorkflow.Types;
using Logging;
-using MetricsPlugin;
using Utils;
-namespace CodexPlugin
+namespace CodexClient
{
- public interface ICodexNode : IHasContainer, IHasMetricsScrapeTarget, IHasEthAddress
+ public partial interface ICodexNode : IHasEthAddress, IHasMetricsScrapeTarget
{
string GetName();
+ string GetImageName();
string GetPeerId();
DebugInfo GetDebugInfo(bool log = false);
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
@@ -34,70 +28,67 @@ namespace CodexPlugin
void ConnectToPeer(ICodexNode node);
DebugInfoVersion Version { get; }
IMarketplaceAccess Marketplace { get; }
- CrashWatcher CrashWatcher { get; }
- PodInfo GetPodInfo();
ITransferSpeeds TransferSpeeds { get; }
EthAccount EthAccount { get; }
+ StoragePurchase GetPurchaseStatus(string purchaseId);
+
+ Address GetDiscoveryEndpoint();
+ Address GetApiEndpoint();
+ Address GetListenEndpoint();
///
/// 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);
+ IDownloadedLog DownloadLog(string additionalName = "");
+ bool HasCrashed();
}
public class CodexNode : ICodexNode
{
private const string UploadFailedMessage = "Unable to store block";
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;
+ private readonly IFileManager fileManager;
- public CodexNode(IPluginTools tools, CodexAccess codexAccess, CodexNodeGroup group, IMarketplaceAccess marketplaceAccess, ICodexNodeHooks hooks, EthAccount? ethAccount)
+ public CodexNode(ILog log, CodexAccess codexAccess, IFileManager fileManager, IMarketplaceAccess marketplaceAccess, ICodexNodeHooks hooks)
{
- this.tools = tools;
- this.ethAccount = ethAccount;
- CodexAccess = codexAccess;
- Group = group;
+ 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()
{
- hooks.OnNodeStarting(Container.Recipe.RecipeCreatedUtc, Container.Recipe.Image, ethAccount);
+ hooks.OnNodeStarting(codexAccess.GetStartUtc(), codexAccess.GetImageName(), codexAccess.GetEthAccount());
}
public void Initialize()
{
- hooks.OnNodeStarted(peerId, nodeId);
+ InitializePeerNodeId();
+ InitializeLogReplacements();
+
+ hooks.OnNodeStarted(this, 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 StoragePurchase GetPurchaseStatus(string purchaseId)
{
- get
- {
- return new MetricsScrapeTarget(CodexAccess.Container.Containers.First(), CodexContainerRecipe.MetricsPortTag);
- }
+ return codexAccess.GetPurchaseStatus(purchaseId);
}
public EthAddress EthAddress
@@ -105,7 +96,7 @@ namespace CodexPlugin
get
{
EnsureMarketplace();
- return ethAccount!.EthAddress;
+ return codexAccess.GetEthAccount()!.EthAddress;
}
}
@@ -114,13 +105,18 @@ namespace CodexPlugin
get
{
EnsureMarketplace();
- return ethAccount!;
+ return codexAccess.GetEthAccount()!;
}
}
public string GetName()
{
- return Container.Name;
+ return codexAccess.GetName();
+ }
+
+ public string GetImageName()
+ {
+ return codexAccess.GetImageName();
}
public string GetPeerId()
@@ -130,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));
@@ -141,25 +137,20 @@ 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)
{
- 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();
@@ -171,7 +162,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;
@@ -189,17 +180,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);
@@ -211,7 +197,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)
@@ -221,7 +207,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;
});
@@ -232,17 +218,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)
@@ -251,47 +237,53 @@ 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 PodInfo GetPodInfo()
+ public void DeleteDataDirFolder()
{
- return CodexAccess.GetPodInfo();
- }
-
- public void DeleteRepoFolder()
- {
- CodexAccess.DeleteRepoFolder();
+ codexAccess.DeleteDataDirFolder();
}
public void Stop(bool waitTillStopped)
{
Log("Stopping...");
hooks.OnNodeStopping();
-
- CrashWatcher.Stop();
- Group.Stop(this, waitTillStopped);
+ codexAccess.Stop(waitTillStopped);
}
- public void EnsureOnlineGetVersionResponse()
+ public IDownloadedLog DownloadLog(string additionalName = "")
{
- var debugInfo = Time.Retry(CodexAccess.GetDebugInfo, "ensure online");
- peerId = debugInfo.Id;
- nodeId = debugInfo.Table.LocalNode.NodeId;
- var nodeName = CodexAccess.Container.Name;
+ return codexAccess.DownloadLog(additionalName);
+ }
- if (!debugInfo.Version.IsValid())
- {
- throw new Exception($"Invalid version information received from Codex node {GetName()}: {debugInfo.Version}");
- }
+ public Address GetDiscoveryEndpoint()
+ {
+ return codexAccess.GetDiscoveryEndpoint();
+ }
- 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;
+ public Address GetApiEndpoint()
+ {
+ return codexAccess.GetApiEndpoint();
+ }
+
+ public Address GetListenEndpoint()
+ {
+ 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.HasCrashed();
}
public override string ToString()
@@ -299,22 +291,44 @@ 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)
{
- // 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();
}
- 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.
@@ -322,7 +336,7 @@ namespace CodexPlugin
var cts = new CancellationTokenSource();
var downloadTask = Task.Run(() =>
{
- using var downloadStream = CodexAccess.DownloadFile(contentId, onFailure);
+ using var downloadStream = codexAccess.DownloadFile(contentId);
downloadStream.CopyTo(fileStream);
}, cts.Token);
@@ -336,9 +350,9 @@ namespace CodexPlugin
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;
}
}
@@ -378,16 +392,12 @@ 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)
{
log.Log(msg);
}
-
- private void DoNothing(Failure failure)
- {
- }
}
}
diff --git a/ProjectPlugins/CodexClient/CodexNodeFactory.cs b/ProjectPlugins/CodexClient/CodexNodeFactory.cs
new file mode 100644
index 00000000..22e7a69e
--- /dev/null
+++ b/ProjectPlugins/CodexClient/CodexNodeFactory.cs
@@ -0,0 +1,47 @@
+using CodexClient.Hooks;
+using FileUtils;
+using Logging;
+using WebUtils;
+
+namespace CodexClient
+{
+ public class CodexNodeFactory
+ {
+ private readonly ILog log;
+ private readonly IFileManager fileManager;
+ private readonly CodexHooksFactory hooksFactory;
+ private readonly IHttpFactory httpFactory;
+ private readonly IProcessControlFactory processControlFactory;
+
+ public CodexNodeFactory(ILog log, IFileManager fileManager, CodexHooksFactory hooksFactory, IHttpFactory httpFactory, IProcessControlFactory processControlFactory)
+ {
+ this.log = log;
+ this.fileManager = fileManager;
+ 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 = hooksFactory.CreateHooks(access.GetName());
+ var marketplaceAccess = CreateMarketplaceAccess(instance, access, hooks);
+ var node = new CodexNode(log, access, fileManager, marketplaceAccess, hooks);
+ node.Initialize();
+ return node;
+ }
+
+ 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/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 76%
rename from ProjectPlugins/CodexPlugin/Hooks/CodexHooksFactory.cs
rename to ProjectPlugins/CodexClient/Hooks/CodexHooksFactory.cs
index b3a718ea..0557f138 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
{
@@ -10,11 +9,14 @@ namespace CodexPlugin.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);
}
}
@@ -44,7 +46,7 @@ namespace CodexPlugin.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
new file mode 100644
index 00000000..926a5d1d
--- /dev/null
+++ b/ProjectPlugins/CodexClient/Hooks/CodexNodeHooks.cs
@@ -0,0 +1,78 @@
+using Utils;
+
+namespace CodexClient.Hooks
+{
+ public interface ICodexNodeHooks
+ {
+ void OnNodeStarting(DateTime startUtc, string image, EthAccount? ethAccount);
+ void OnNodeStarted(ICodexNode node, string peerId, string nodeId);
+ void OnNodeStopping();
+ void OnFileUploading(string uid, ByteSize size);
+ void OnFileUploaded(string uid, ByteSize size, ContentId cid);
+ void OnFileDownloading(ContentId cid);
+ void OnFileDownloaded(ByteSize size, ContentId cid);
+ void OnStorageContractSubmitted(StoragePurchaseContract storagePurchaseContract);
+ 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/Mapper.cs b/ProjectPlugins/CodexClient/Mapper.cs
similarity index 94%
rename from ProjectPlugins/CodexPlugin/Mapper.cs
rename to ProjectPlugins/CodexClient/Mapper.cs
index b80ba24d..a0c34777 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 96%
rename from ProjectPlugins/CodexPlugin/MarketplaceAccess.cs
rename to ProjectPlugins/CodexClient/MarketplaceAccess.cs
index a657e9b0..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
{
@@ -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/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 c44b38a3..4c82947b 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..2ddfab2e
--- /dev/null
+++ b/ProjectPlugins/CodexClient/ProcessControl.cs
@@ -0,0 +1,46 @@
+using Logging;
+
+namespace CodexClient
+{
+ public interface IProcessControlFactory
+ {
+ IProcessControl CreateProcessControl(ICodexInstance instance);
+ }
+
+ public interface IProcessControl
+ {
+ void Stop(bool waitTillStopped);
+ IDownloadedLog DownloadLog(LogFile file);
+ 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/CodexPlugin/StoragePurchaseContract.cs b/ProjectPlugins/CodexClient/StoragePurchaseContract.cs
similarity index 90%
rename from ProjectPlugins/CodexPlugin/StoragePurchaseContract.cs
rename to ProjectPlugins/CodexClient/StoragePurchaseContract.cs
index 07a12bac..0455b08f 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
{
@@ -15,8 +13,9 @@ namespace CodexPlugin
StoragePurchase GetStatus();
void WaitForStorageContractSubmitted();
void WaitForStorageContractStarted();
- void WaitForStorageContractFinished(ICodexContracts contracts);
+ void WaitForStorageContractFinished();
void WaitForContractFailed();
+ StoragePurchase GetPurchaseStatus();
}
public class StoragePurchaseContract : IStoragePurchaseContract
@@ -71,7 +70,7 @@ namespace CodexPlugin
AssertDuration(SubmittedToStarted, timeout, nameof(SubmittedToStarted));
}
- public void WaitForStorageContractFinished(ICodexContracts contracts)
+ public void WaitForStorageContractFinished()
{
if (!contractStartedUtc.HasValue)
{
@@ -83,13 +82,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()
@@ -103,9 +95,9 @@ namespace CodexPlugin
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/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/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/ApiChecker.cs b/ProjectPlugins/CodexPlugin/ApiChecker.cs
index 19112340..778977d3 100644
--- a/ProjectPlugins/CodexPlugin/ApiChecker.cs
+++ b/ProjectPlugins/CodexPlugin/ApiChecker.cs
@@ -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/BinaryCodexStarter.cs b/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs
new file mode 100644
index 00000000..617cb701
--- /dev/null
+++ b/ProjectPlugins/CodexPlugin/BinaryCodexStarter.cs
@@ -0,0 +1,156 @@
+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 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)
+ {
+ this.pluginTools = pluginTools;
+ this.processControlMap = processControlMap;
+ }
+
+ public ICodexInstance[] BringOnline(CodexSetup codexSetup)
+ {
+ lock (_lock)
+ {
+ LogSeparator();
+ Log($"Starting {codexSetup.Describe()}...");
+
+ return StartCodexBinaries(codexSetup, codexSetup.NumberOfNodes);
+ }
+ }
+
+ public void Decommission()
+ {
+ lock (_lock)
+ {
+ processControlMap.StopAll();
+ }
+ }
+
+ 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 name = GetName(config);
+ var dataDir = Path.Combine(dataParentDir, $"datadir_{numberSource.GetNextNumber()}");
+ var pconfig = new CodexProcessConfig(name, freePortFinder, dataDir);
+ Log(pconfig);
+
+ var factory = new CodexProcessRecipe(pconfig, codexExePath);
+ var recipe = factory.Initialize(config);
+
+ 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", pconfig.LocalIpAddrs.ToString(), 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(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("----------------------------------------------------------------------------");
+ }
+
+ 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
new file mode 100644
index 00000000..c4ed3ee1
--- /dev/null
+++ b/ProjectPlugins/CodexPlugin/BinaryProcessControl.cs
@@ -0,0 +1,93 @@
+using System.Diagnostics;
+using CodexClient;
+using Logging;
+
+namespace CodexPlugin
+{
+ public class BinaryProcessControl : IProcessControl
+ {
+ private readonly LogFile logFile;
+ private readonly Process process;
+ private readonly CodexProcessConfig config;
+ private List logBuffer = new List();
+ private readonly object bufferLock = new object();
+ private readonly List streamTasks = new List();
+ private bool running;
+
+ 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)
+ {
+ while (running)
+ {
+ var line = reader.ReadLine();
+ if (!string.IsNullOrEmpty(line))
+ {
+ lock (bufferLock)
+ {
+ logBuffer.Add(line);
+ }
+ }
+ }
+ }
+
+ private void WriteLog()
+ {
+ while (running || logBuffer.Count > 0)
+ {
+ if (logBuffer.Count > 0)
+ {
+ List lines = null!;
+ lock (bufferLock)
+ {
+ lines = logBuffer;
+ logBuffer = new List();
+ }
+
+ logFile.WriteRawMany(lines);
+ }
+ else Thread.Sleep(100);
+ }
+ }
+
+ public void DeleteDataDirFolder()
+ {
+ if (!Directory.Exists(config.DataDir)) throw new Exception("datadir not found");
+ Directory.Delete(config.DataDir, true);
+ }
+
+ public IDownloadedLog DownloadLog(LogFile file)
+ {
+ return new DownloadedLog(logFile, config.Name);
+ }
+
+ public bool HasCrashed()
+ {
+ return process.HasExited;
+ }
+
+ public void Stop(bool waitTillStopped)
+ {
+ running = false;
+ process.Kill();
+
+ if (waitTillStopped)
+ {
+ process.WaitForExit();
+ foreach (var t in streamTasks) t.Wait();
+ }
+
+ DeleteDataDirFolder();
+ }
+ }
+}
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/CodexDeployment.cs b/ProjectPlugins/CodexPlugin/CodexDeployment.cs
index 7cbf4506..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;
@@ -9,7 +10,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 +21,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 +30,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/ProjectPlugins/CodexPlugin/CodexExePath.cs b/ProjectPlugins/CodexPlugin/CodexExePath.cs
new file mode 100644
index 00000000..79e9d4d8
--- /dev/null
+++ b/ProjectPlugins/CodexPlugin/CodexExePath.cs
@@ -0,0 +1,29 @@
+namespace CodexPlugin
+{
+ public class CodexExePath
+ {
+ 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 selectedPath;
+ }
+ }
+}
diff --git a/ProjectPlugins/CodexPlugin/CodexInstanceContainerExtension.cs b/ProjectPlugins/CodexPlugin/CodexInstanceContainerExtension.cs
new file mode 100644
index 00000000..2104823e
--- /dev/null
+++ b/ProjectPlugins/CodexPlugin/CodexInstanceContainerExtension.cs
@@ -0,0 +1,45 @@
+using CodexClient;
+using KubernetesWorkflow.Types;
+using Utils;
+
+namespace CodexPlugin
+{
+ public static class CodexInstanceContainerExtension
+ {
+ public static ICodexInstance CreateFromPod(RunningPod pod)
+ {
+ 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)
+ );
+ }
+
+ // 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
+ //);
+
+ private static Address? GetMetricsEndpoint(RunningContainer container)
+ {
+ try
+ {
+ return container.GetInternalAddress(CodexContainerRecipe.MetricsPortTag);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+ }
+}
diff --git a/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs b/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs
deleted file mode 100644
index f55180c6..00000000
--- a/ProjectPlugins/CodexPlugin/CodexNodeFactory.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using CodexPlugin.Hooks;
-using Core;
-using GethPlugin;
-using KubernetesWorkflow;
-using KubernetesWorkflow.Types;
-
-namespace CodexPlugin
-{
- public interface ICodexNodeFactory
- {
- CodexNode CreateOnlineCodexNode(CodexAccess access, CodexNodeGroup group);
- CrashWatcher CreateCrashWatcher(RunningContainer c);
- }
-
- 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, CodexNodeGroup group)
- {
- 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);
- }
-
- private IMarketplaceAccess GetMarketplaceAccess(CodexAccess codexAccess, EthAccount? ethAccount, ICodexNodeHooks hooks)
- {
- if (ethAccount == 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 CrashWatcher CreateCrashWatcher(RunningContainer c)
- {
- return tools.CreateWorkflow().CreateCrashWatcher(c);
- }
- }
-}
diff --git a/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs b/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs
index e4b999b8..4a8bd9bb 100644
--- a/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs
+++ b/ProjectPlugins/CodexPlugin/CodexNodeGroup.cs
@@ -1,25 +1,23 @@
-using Core;
-using KubernetesWorkflow.Types;
-using MetricsPlugin;
+using CodexClient;
+using Core;
using System.Collections;
+using Utils;
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 readonly ICodexNode[] nodes;
- public CodexNodeGroup(CodexStarter starter, IPluginTools tools, RunningPod[] containers, ICodexNodeFactory codexNodeFactory)
+ public CodexNodeGroup(IPluginTools tools, ICodexNode[] nodes)
{
- this.starter = starter;
- Containers = containers;
- Nodes = containers.Select(c => CreateOnlineCodexNode(c, tools, codexNodeFactory)).ToArray();
+ this.nodes = nodes;
Version = new DebugInfoVersion();
}
@@ -31,25 +29,23 @@ namespace CodexPlugin
}
}
- public void BringOffline(bool waitTillStopped)
+ public void Stop(bool waitTillStopped)
{
- starter.BringOffline(this, waitTillStopped);
- // Clear everything. Prevent accidental use.
- Nodes = Array.Empty();
- Containers = null!;
+ foreach (var node in Nodes) node.Stop(waitTillStopped);
}
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();
+ node.Stop(waitTillStopped);
}
- 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[] GetMetricsScrapeTargets()
+ {
+ return Nodes.Select(n => n.GetMetricsScrapeTarget()).ToArray();
+ }
public IEnumerator GetEnumerator()
{
@@ -63,12 +59,11 @@ 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();
var versionResponses = Nodes.Select(n => n.Version);
var first = versionResponses.First();
@@ -79,16 +74,6 @@ 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;
}
}
}
diff --git a/ProjectPlugins/CodexPlugin/CodexPlugin.cs b/ProjectPlugins/CodexPlugin/CodexPlugin.cs
index 04c2d5f5..02fbeaa6 100644
--- a/ProjectPlugins/CodexPlugin/CodexPlugin.cs
+++ b/ProjectPlugins/CodexPlugin/CodexPlugin.cs
@@ -1,48 +1,68 @@
-using CodexPlugin.Hooks;
+using CodexClient;
+using CodexClient.Hooks;
using Core;
-using KubernetesWorkflow.Types;
namespace CodexPlugin
{
public class CodexPlugin : IProjectPlugin, IHasLogPrefix, IHasMetadata
{
- private readonly CodexStarter codexStarter;
+ private const bool UseContainers = true;
+
+ private readonly ICodexStarter codexStarter;
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 CodexStarter(tools);
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) ";
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()
{
+ codexStarter.Decommission();
}
- 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(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 codexWrapper.WrapCodexInstances(instances);
}
public void WireUpMarketplace(ICodexNodeGroup result, Action setup)
@@ -62,9 +82,10 @@ namespace CodexPlugin
}
}
- public void SetCodexHooksProvider(ICodexHooksProvider hooksProvider)
+ public void AddCodexHooksProvider(ICodexHooksProvider hooksProvider)
{
- codexStarter.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/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/CodexProcessRecipe.cs b/ProjectPlugins/CodexPlugin/CodexProcessRecipe.cs
new file mode 100644
index 00000000..dbbc497a
--- /dev/null
+++ b/ProjectPlugins/CodexPlugin/CodexProcessRecipe.cs
@@ -0,0 +1,160 @@
+using System.Net.Sockets;
+using System.Net;
+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 CodexProcessConfig
+ {
+ public CodexProcessConfig(string name, FreePortFinder freePortFinder, string dataDir)
+ {
+ ApiPort = freePortFinder.GetNextFreePort();
+ DiscPort = freePortFinder.GetNextFreePort();
+ 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; }
+ public int DiscPort { get; }
+ 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, CodexExePath codexExePath)
+ {
+ this.pc = pc;
+ this.codexExePath = codexExePath;
+ }
+
+ public ProcessRecipe Initialize(CodexStartupConfig config)
+ {
+ args.Clear();
+
+ AddArg("--api-port", pc.ApiPort);
+ AddArg("--api-bindaddr", "0.0.0.0");
+
+ AddArg("--data-dir", pc.DataDir);
+
+ AddArg("--disc-port", pc.DiscPort);
+ AddArg("--log-level", config.LogLevelWithTopics());
+
+ // This makes the node announce itself to its local IP address.
+ AddArg("--nat", $"extip:{pc.LocalIpAddrs.ToStringInvariant()}");
+
+ AddArg("--listen-addrs", $"/ip4/0.0.0.0/tcp/{pc.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: codexExePath.Get(),
+ 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/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/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/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/CodexStarter.cs b/ProjectPlugins/CodexPlugin/ContainerCodexStarter.cs
similarity index 50%
rename from ProjectPlugins/CodexPlugin/CodexStarter.cs
rename to ProjectPlugins/CodexPlugin/ContainerCodexStarter.cs
index 4c433725..0627bc1f 100644
--- a/ProjectPlugins/CodexPlugin/CodexStarter.cs
+++ b/ProjectPlugins/CodexPlugin/ContainerCodexStarter.cs
@@ -1,29 +1,26 @@
-using CodexPlugin.Hooks;
+using CodexClient;
using Core;
-using GethPlugin;
using KubernetesWorkflow;
using KubernetesWorkflow.Types;
-using Logging;
+using Utils;
namespace CodexPlugin
{
- public class CodexStarter
+ public class ContainerCodexStarter : ICodexStarter
{
private readonly IPluginTools pluginTools;
+ private readonly ProcessControlMap processControlMap;
private readonly CodexContainerRecipe recipe = new CodexContainerRecipe();
private readonly ApiChecker apiChecker;
- private DebugInfoVersion? versionResponse;
- public CodexStarter(IPluginTools pluginTools)
+ public ContainerCodexStarter(IPluginTools pluginTools, ProcessControlMap processControlMap)
{
this.pluginTools = pluginTools;
-
+ this.processControlMap = processControlMap;
apiChecker = new ApiChecker(pluginTools);
}
- public CodexHooksFactory HooksFactory { get; } = new CodexHooksFactory();
-
- public RunningPod[] BringOnline(CodexSetup codexSetup)
+ public ICodexInstance[] BringOnline(CodexSetup codexSetup)
{
LogSeparator();
Log($"Starting {codexSetup.Describe()}...");
@@ -43,51 +40,11 @@ namespace CodexPlugin
}
LogSeparator();
- return containers;
+ return containers.Select(CreateInstance).ToArray();
}
- public ICodexNodeGroup WrapCodexContainers(CoreInterface coreInterface, RunningPod[] containers)
+ public void Decommission()
{
- var codexNodeFactory = new CodexNodeFactory(pluginTools, HooksFactory);
-
- var group = CreateCodexGroup(coreInterface, containers, codexNodeFactory);
-
- Log($"Codex version: {group.Version}");
- versionResponse = group.Version;
-
- return group;
- }
-
- public void BringOffline(CodexNodeGroup group, bool waitTillStopped)
- {
- Log($"Stopping {group.Describe()}...");
- StopCrashWatcher(group);
- var workflow = pluginTools.CreateWorkflow();
- foreach (var c in group.Containers)
- {
- workflow.Stop(c, waitTillStopped);
- }
- Log("Stopped.");
- }
-
- public void Stop(RunningPod 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;
- return recipe.Image;
- }
-
- public string GetCodexRevision()
- {
- if (versionResponse != null) return versionResponse.Revision;
- return "unknown";
}
private StartupConfig CreateStartupConfig(CodexSetup codexSetup)
@@ -118,27 +75,15 @@ namespace CodexPlugin
return workflow.GetPodInfo(rc);
}
- private CodexNodeGroup CreateCodexGroup(CoreInterface coreInterface, RunningPod[] runningContainers, CodexNodeFactory codexNodeFactory)
+ private ICodexInstance CreateInstance(RunningPod pod)
{
- var group = new CodexNodeGroup(this, pluginTools, runningContainers, codexNodeFactory);
-
- try
+ var instance = CodexInstanceContainerExtension.CreateFromPod(pod);
+ var processControl = new CodexContainerProcessControl(pluginTools, pod, onStop: () =>
{
- Stopwatch.Measure(pluginTools.GetLog(), "EnsureOnline", group.EnsureOnline);
- }
- catch
- {
- CodexNodesNotOnline(coreInterface, runningContainers);
- throw;
- }
-
- return group;
- }
-
- private void CodexNodesNotOnline(CoreInterface coreInterface, RunningPod[] runningContainers)
- {
- Log("Codex nodes failed to start");
- foreach (var container in runningContainers.First().Containers) coreInterface.DownloadLog(container);
+ processControlMap.Remove(instance);
+ });
+ processControlMap.Add(instance, processControl);
+ return instance;
}
private void LogSeparator()
@@ -157,13 +102,5 @@ namespace CodexPlugin
{
pluginTools.GetLog().Log(message);
}
-
- private void StopCrashWatcher(CodexNodeGroup group)
- {
- foreach (var node in group)
- {
- node.CrashWatcher.Stop();
- }
- }
}
}
diff --git a/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs b/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs
index 6fb8842e..383ac38b 100644
--- a/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs
+++ b/ProjectPlugins/CodexPlugin/CoreInterfaceExtensions.cs
@@ -1,19 +1,19 @@
-using CodexPlugin.Hooks;
+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(instances);
}
public static ICodexNode StartCodexNode(this CoreInterface ci)
@@ -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/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;
+ }
+ }
+}
diff --git a/ProjectPlugins/CodexPlugin/Hooks/CodexNodeHooks.cs b/ProjectPlugins/CodexPlugin/Hooks/CodexNodeHooks.cs
deleted file mode 100644
index b2bf6170..00000000
--- a/ProjectPlugins/CodexPlugin/Hooks/CodexNodeHooks.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using GethPlugin;
-using Utils;
-
-namespace CodexPlugin.Hooks
-{
- public interface ICodexNodeHooks
- {
- void OnNodeStarting(DateTime startUtc, string image, EthAccount? ethAccount);
- void OnNodeStarted(string peerId, string nodeId);
- void OnNodeStopping();
- void OnFileUploading(string uid, ByteSize size);
- void OnFileUploaded(string uid, ByteSize size, ContentId cid);
- void OnFileDownloading(ContentId cid);
- void OnFileDownloaded(ByteSize size, ContentId cid);
- void OnStorageContractSubmitted(StoragePurchaseContract storagePurchaseContract);
- void OnStorageContractUpdated(StoragePurchase purchaseStatus);
- void OnStorageAvailabilityCreated(StorageAvailability response);
- }
-}
diff --git a/ProjectPlugins/CodexPlugin/ICodexStarter.cs b/ProjectPlugins/CodexPlugin/ICodexStarter.cs
new file mode 100644
index 00000000..c88cd382
--- /dev/null
+++ b/ProjectPlugins/CodexPlugin/ICodexStarter.cs
@@ -0,0 +1,10 @@
+using CodexClient;
+
+namespace CodexPlugin
+{
+ public interface ICodexStarter
+ {
+ ICodexInstance[] BringOnline(CodexSetup codexSetup);
+ void Decommission();
+ }
+}
diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexLogConverter.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexLogConverter.cs
index 860180e8..71be7873 100644
--- a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexLogConverter.cs
+++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexLogConverter.cs
@@ -1,5 +1,6 @@
-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 6a6cdfca..db4702d7 100644
--- a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexNodeTranscriptWriter.cs
+++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexNodeTranscriptWriter.cs
@@ -1,5 +1,5 @@
-using CodexPlugin.Hooks;
-using GethPlugin;
+using CodexClient;
+using CodexClient.Hooks;
using OverwatchTranscript;
using Utils;
@@ -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/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/CodexPlugin/ProcessControlMap.cs b/ProjectPlugins/CodexPlugin/ProcessControlMap.cs
new file mode 100644
index 00000000..99498f57
--- /dev/null
+++ b/ProjectPlugins/CodexPlugin/ProcessControlMap.cs
@@ -0,0 +1,37 @@
+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];
+ }
+
+ public void StopAll()
+ {
+ var pcs = processControlMap.Values.ToArray();
+ processControlMap.Clear();
+
+ foreach (var c in pcs) c.Stop(waitTillStopped: true);
+ }
+ }
+}
diff --git a/ProjectPlugins/CodexPluginPrebuild/Program.cs b/ProjectPlugins/CodexPluginPrebuild/Program.cs
index 3df9f235..6e9d3b9b 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(clientRoot, "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/ProjectPlugins/MetricsPlugin/CoreInterfaceExtensions.cs b/ProjectPlugins/MetricsPlugin/CoreInterfaceExtensions.cs
index c8075a02..76f6c108 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
{
@@ -8,35 +9,35 @@ 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 IMetricsScrapeTarget[] scrapeTargets)
+ public static RunningPod DeployMetricsCollector(this CoreInterface ci, TimeSpan scrapeInterval, params Address[] scrapeTargets)
{
return Plugin(ci).DeployMetricsCollector(scrapeTargets, scrapeInterval);
}
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, IMetricsScrapeTarget scrapeTarget)
+ public static IMetricsAccess WrapMetricsCollector(this CoreInterface ci, RunningPod metricsPod, Address scrapeTarget)
{
return Plugin(ci).WrapMetricsCollectorDeployment(metricsPod, scrapeTarget);
}
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 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/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/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..0f0d11db 100644
--- a/ProjectPlugins/MetricsPlugin/MetricsQuery.cs
+++ b/ProjectPlugins/MetricsPlugin/MetricsQuery.cs
@@ -3,6 +3,8 @@ using IdentityModel;
using KubernetesWorkflow.Types;
using Logging;
using System.Globalization;
+using Utils;
+using WebUtils;
namespace MetricsPlugin
{
@@ -23,7 +25,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 +55,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 +141,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 +174,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 +184,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
deleted file mode 100644
index 5c9cbd14..00000000
--- a/ProjectPlugins/MetricsPlugin/MetricsScrapeTarget.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using KubernetesWorkflow.Types;
-
-namespace MetricsPlugin
-{
- public interface IMetricsScrapeTarget
- {
- RunningContainer Container { get; }
- string MetricsPortTag { get; }
- }
-
- public interface IHasMetricsScrapeTarget
- {
- IMetricsScrapeTarget 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; }
- }
-}
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/CodexContinuousTests/ContinuousTest.cs b/Tests/CodexContinuousTests/ContinuousTest.cs
index 5b746682..fa39255c 100644
--- a/Tests/CodexContinuousTests/ContinuousTest.cs
+++ b/Tests/CodexContinuousTests/ContinuousTest.cs
@@ -1,15 +1,13 @@
-using CodexPlugin;
-using Core;
-using DistTestCore;
+using CodexClient;
using FileUtils;
using Logging;
using MetricsPlugin;
+using Utils;
namespace ContinuousTests
{
public abstract class ContinuousTestLongTimeouts : ContinuousTest
{
- public override ITimeSet TimeSet => new LongTimeSet();
}
public abstract class ContinuousTest
@@ -44,16 +42,15 @@ 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(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..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
{
@@ -37,7 +38,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();
@@ -68,7 +69,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..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
{
@@ -24,12 +23,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);
@@ -40,7 +33,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
{
@@ -59,7 +52,7 @@ namespace ContinuousTests
}
catch
{
- DownloadLog(node.Container);
+ node.DownloadLog();
throw;
}
}
diff --git a/Tests/CodexContinuousTests/SingleTestRun.cs b/Tests/CodexContinuousTests/SingleTestRun.cs
index e9afb313..0606c8e4 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
{
@@ -125,13 +126,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.");
}
}
@@ -204,7 +206,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(":"));
@@ -286,29 +287,29 @@ 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()
{
- 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()
{
var number = handle.Test.RequiredNumberOfNodes;
- var containers = config.CodexDeployment.CodexInstances.Select(i => i.Pod).ToList();
- if (number == -1) return containers.ToArray();
+ var instances = config.CodexDeployment.CodexInstances.ToList();
+ if (number == -1) return instances.ToArray();
- var result = new RunningPod[number];
+ var result = new ICodexInstance[number];
for (var i = 0; i < number; i++)
{
- result[i] = containers.PickOneRandom();
+ result[i] = instances.PickOneRandom();
}
return result;
}
}
-}
\ No newline at end of file
+}
diff --git a/Tests/CodexContinuousTests/StartupChecker.cs b/Tests/CodexContinuousTests/StartupChecker.cs
index ca4c8874..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
{
@@ -39,25 +38,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 +92,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.Container.GetAddress(CodexContainerRecipe.ApiPortTag);
- log.Log($"Checking {n.Container.Name} @ '{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)
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 3d37c635..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;
@@ -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/CodexLongTests/ScalabilityTests/ScalabilityTests.cs b/Tests/CodexLongTests/ScalabilityTests/ScalabilityTests.cs
index c3b05f7a..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,16 +38,16 @@ 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);
- 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();
}
///
diff --git a/Tests/CodexReleaseTests/DataTests/InterruptUploadTest.cs b/Tests/CodexReleaseTests/DataTests/InterruptUploadTest.cs
index ae9c9d58..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;
@@ -32,13 +32,13 @@ 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();
}
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/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 afb6d07a..8ab9c88d 100644
--- a/Tests/CodexReleaseTests/DataTests/TwoClientTest.cs
+++ b/Tests/CodexReleaseTests/DataTests/TwoClientTest.cs
@@ -1,11 +1,7 @@
-using CodexPlugin;
+using CodexClient;
+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
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 cecfbe0a..caeaed9b 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
@@ -36,7 +29,7 @@ namespace CodexReleaseTests.MarketTests
request.WaitForStorageContractStarted();
AssertContractSlotsAreFilledByHosts(request, hosts);
- hosts.BringOffline(waitTillStopped: true);
+ hosts.Stop(waitTillStopped: true);
WaitForSlotFreedEvents();
diff --git a/Tests/CodexReleaseTests/MarketTests/ContractSuccessfulTest.cs b/Tests/CodexReleaseTests/MarketTests/ContractSuccessfulTest.cs
index 70f31531..8431d121 100644
--- a/Tests/CodexReleaseTests/MarketTests/ContractSuccessfulTest.cs
+++ b/Tests/CodexReleaseTests/MarketTests/ContractSuccessfulTest.cs
@@ -1,5 +1,4 @@
-using CodexContractsPlugin;
-using CodexPlugin;
+using CodexClient;
using NUnit.Framework;
using Utils;
@@ -30,7 +29,7 @@ namespace CodexReleaseTests.MarketTests
request.WaitForStorageContractStarted();
AssertContractSlotsAreFilledByHosts(request, hosts);
- request.WaitForStorageContractFinished(GetContracts());
+ request.WaitForStorageContractFinished();
AssertClientHasPaidForContract(pricePerBytePerSecond, client, request, hosts);
AssertHostsWerePaidForContract(pricePerBytePerSecond, request, hosts);
diff --git a/Tests/CodexReleaseTests/MarketTests/MarketplaceAutoBootstrapDistTest.cs b/Tests/CodexReleaseTests/MarketTests/MarketplaceAutoBootstrapDistTest.cs
index 39db6a65..a5fa41e7 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,
minPricePerBytePerSecond: 1.TstWei(),
diff --git a/Tests/CodexReleaseTests/MarketTests/MultipleContractsTest.cs b/Tests/CodexReleaseTests/MarketTests/MultipleContractsTest.cs
index 986081d7..6ad10643 100644
--- a/Tests/CodexReleaseTests/MarketTests/MultipleContractsTest.cs
+++ b/Tests/CodexReleaseTests/MarketTests/MultipleContractsTest.cs
@@ -1,6 +1,4 @@
-using CodexContractsPlugin;
-using CodexPlugin;
-using GethPlugin;
+using CodexClient;
using NUnit.Framework;
using Utils;
@@ -35,7 +33,15 @@ 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();
+ //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);
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/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/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/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..3d642d20 100644
--- a/Tests/DistTestCore/TestLifecycle.cs
+++ b/Tests/DistTestCore/TestLifecycle.cs
@@ -4,7 +4,9 @@ using FileUtils;
using KubernetesWorkflow;
using KubernetesWorkflow.Recipe;
using KubernetesWorkflow.Types;
+using Logging;
using Utils;
+using WebUtils;
namespace DistTestCore
{
@@ -17,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;
@@ -36,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; }
@@ -111,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/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/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 b2cb76f8..b4ad44fa 100644
--- a/Tests/ExperimentalTests/BasicTests/ExampleTests.cs
+++ b/Tests/ExperimentalTests/BasicTests/ExampleTests.cs
@@ -1,11 +1,12 @@
-using CodexPlugin;
+using CodexClient;
+using CodexPlugin;
+using CodexTests;
using DistTestCore;
-using GethPlugin;
using MetricsPlugin;
using NUnit.Framework;
using Utils;
-namespace CodexTests.BasicTests
+namespace ExperimentalTests.BasicTests
{
[TestFixture]
public class ExampleTests : CodexDistTest
@@ -20,7 +21,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/BasicTests/MarketplaceTests.cs b/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs
index a4d63f57..f8ef52d5 100644
--- a/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs
+++ b/Tests/ExperimentalTests/BasicTests/MarketplaceTests.cs
@@ -1,12 +1,14 @@
-using CodexContractsPlugin;
+using CodexClient;
+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
@@ -23,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")
@@ -80,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));
@@ -107,7 +109,15 @@ namespace CodexTests.BasicTests
AssertStorageRequest(request, purchase, contracts, client);
AssertContractSlot(contracts, request, 0);
- purchaseContract.WaitForStorageContractFinished(contracts);
+ purchaseContract.WaitForStorageContractFinished();
+
+ // 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/BasicTests/PyramidTests.cs b/Tests/ExperimentalTests/BasicTests/PyramidTests.cs
index 306d9d44..95d43e83 100644
--- a/Tests/ExperimentalTests/BasicTests/PyramidTests.cs
+++ b/Tests/ExperimentalTests/BasicTests/PyramidTests.cs
@@ -1,13 +1,9 @@
-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 CodexTests.BasicTests
+namespace ExperimentalTests.BasicTests
{
[TestFixture]
public class PyramidTests : CodexDistTest
@@ -51,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/CodexDistTest.cs b/Tests/ExperimentalTests/CodexDistTest.cs
index 0ceec87d..99671edb 100644
--- a/Tests/ExperimentalTests/CodexDistTest.cs
+++ b/Tests/ExperimentalTests/CodexDistTest.cs
@@ -1,4 +1,6 @@
using BlockchainUtils;
+using CodexClient;
+using CodexClient.Hooks;
using CodexContractsPlugin;
using CodexNetDeployer;
using CodexPlugin;
@@ -19,11 +21,81 @@ 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()
@@ -105,7 +189,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 ");
@@ -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);
}
@@ -203,7 +287,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 +299,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/DownloadConnectivityTests/DatalayerReliabilityTests.cs b/Tests/ExperimentalTests/DownloadConnectivityTests/DatalayerReliabilityTests.cs
index 6c619969..d218e6ec 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;
@@ -14,15 +14,15 @@ namespace ExperimentalTests.DownloadConnectivityTests
[Test]
[Combinatorial]
public void SingleSetTest(
- [Values(1, 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());
- 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)
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 00dacd22..ec128154 100644
--- a/Tests/ExperimentalTests/DownloadConnectivityTests/FullyConnectedDownloadTests.cs
+++ b/Tests/ExperimentalTests/DownloadConnectivityTests/FullyConnectedDownloadTests.cs
@@ -1,10 +1,10 @@
-using CodexContractsPlugin;
-using CodexPlugin;
-using GethPlugin;
+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 29618947..a206d475 100644
--- a/Tests/ExperimentalTests/DownloadConnectivityTests/MultiswarmTests.cs
+++ b/Tests/ExperimentalTests/DownloadConnectivityTests/MultiswarmTests.cs
@@ -1,10 +1,11 @@
-using CodexPlugin;
+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);
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 f0261a7f..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;
@@ -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/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..bf2ed0e1 100644
--- a/Tests/ExperimentalTests/PeerDiscoveryTests/LayeredDiscoveryTests.cs
+++ b/Tests/ExperimentalTests/PeerDiscoveryTests/LayeredDiscoveryTests.cs
@@ -1,7 +1,8 @@
-using CodexPlugin;
+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 6ec35169..901504c7 100644
--- a/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs
+++ b/Tests/ExperimentalTests/PeerDiscoveryTests/PeerDiscoveryTests.cs
@@ -1,9 +1,10 @@
using CodexContractsPlugin;
-using CodexPlugin;
-using GethPlugin;
+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 24d516f0..abebac0c 100644
--- a/Tests/ExperimentalTests/UtilityTests/DiscordBotTests.cs
+++ b/Tests/ExperimentalTests/UtilityTests/DiscordBotTests.cs
@@ -1,6 +1,8 @@
-using CodexContractsPlugin;
+using CodexClient;
+using CodexContractsPlugin;
using CodexDiscordBotPlugin;
using CodexPlugin;
+using CodexTests;
using Core;
using DiscordRewards;
using DistTestCore;
@@ -11,7 +13,7 @@ using Newtonsoft.Json;
using NUnit.Framework;
using Utils;
-namespace CodexTests.UtilityTests
+namespace ExperimentalTests.UtilityTests
{
[TestFixture]
public class DiscordBotTests : AutoBootstrapDistTest
@@ -19,12 +21,12 @@ 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);
private readonly List receivedEvents = new List();
-
+
[Test]
[DontDownloadLogs]
[Ignore("Used to debug testnet bots.")]
@@ -45,7 +47,16 @@ namespace CodexTests.UtilityTests
var purchaseContract = ClientPurchasesStorage(client);
purchaseContract.WaitForStorageContractStarted();
- purchaseContract.WaitForStorageContractFinished(contracts);
+ purchaseContract.WaitForStorageContractFinished();
+
+ // 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();
@@ -228,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 acc73d34..06f220e4 100644
--- a/Tests/ExperimentalTests/UtilityTests/LogHelperTests.cs
+++ b/Tests/ExperimentalTests/UtilityTests/LogHelperTests.cs
@@ -1,8 +1,9 @@
-using CodexPlugin;
+using CodexClient;
+using CodexTests;
using NUnit.Framework;
using Utils;
-namespace CodexTests.UtilityTests
+namespace ExperimentalTests.UtilityTests
{
[TestFixture]
public class LogHelperTests : AutoBootstrapDistTest
@@ -35,7 +36,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/Tests/ExperimentalTests/UtilityTests/NetworkIsolationTest.cs b/Tests/ExperimentalTests/UtilityTests/NetworkIsolationTest.cs
index c100143c..ea16dbb4 100644
--- a/Tests/ExperimentalTests/UtilityTests/NetworkIsolationTest.cs
+++ b/Tests/ExperimentalTests/UtilityTests/NetworkIsolationTest.cs
@@ -1,9 +1,10 @@
-using CodexPlugin;
+using CodexClient;
+using CodexPlugin;
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.
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/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 6d3a4a74..00000000
--- a/Tools/AutoClient/CodexInstance.cs
+++ /dev/null
@@ -1,161 +0,0 @@
-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()
- {
- CollateralPerByte = app.Config.CollateralPerByte.ToString(),
- Duration = (app.Config.ContractDurationMinutes * 60).ToString(),
- Expiry = (app.Config.ContractExpiryMinutes * 60).ToString(),
- Nodes = app.Config.NumHosts,
- PricePerBytePerSecond = app.Config.PricePerBytePerSecond.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..30567572
--- /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)
+ {
+ 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),
+ PricePerBytePerSecond = app.Config.PricePerBytePerSecond.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 e22c4198..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,59 +75,34 @@ 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);
var port = Convert.ToInt32(endpoint.Substring(splitIndex + 1));
var address = new Address(
+ logName: $"cdx@{host}:{port}",
host: host,
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 ca2723cf..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(':');
@@ -172,21 +176,13 @@ namespace BiblioTech
var port = Convert.ToInt32(endpoint.Substring(splitIndex + 1));
var address = new Address(
+ logName: $"cdx@{host}:{port}",
host: host,
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/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/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/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/CodexNetDeployer/CodexNodeStarter.cs b/Tools/CodexNetDeployer/CodexNodeStarter.cs
index 869337de..92fc77c0 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;
@@ -100,7 +101,7 @@ namespace CodexNetDeployer
if (codexNode != null)
{
Console.WriteLine("Downloading container log.");
- ci.DownloadLog(codexNode);
+ codexNode.DownloadLog();
}
return null;
diff --git a/Tools/CodexNetDeployer/Configuration.cs b/Tools/CodexNetDeployer/Configuration.cs
index 0ea947f7..18c77e6b 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 6d024ab7..753a1371 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;
@@ -181,7 +183,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)
@@ -201,14 +204,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 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);
}
if (!crashes.Any())
@@ -218,7 +219,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.");
}
}
@@ -249,7 +250,7 @@ namespace CodexNetDeployer
}
}
- public class FastHttpTimeSet : ITimeSet
+ public class FastHttpTimeSet : IWebCallTimeSet
{
public TimeSpan HttpCallRetryDelay()
{
@@ -265,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
diff --git a/Tools/KeyMaker/Controllers/KeyController.cs b/Tools/KeyMaker/Controllers/KeyController.cs
index dd218893..c4d1890d 100644
--- a/Tools/KeyMaker/Controllers/KeyController.cs
+++ b/Tools/KeyMaker/Controllers/KeyController.cs
@@ -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;
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}