Compare commits

...
Author SHA1 Message Date
Ben 8847de116d cleanup rewarder bot config 2024-05-16 14:07:14 +02:00
Ben 7d9dcb263d Prints default ArgsUniform values. Applies discord IDs instead of names. 2024-05-16 14:05:58 +02:00
Ben b3771cce32 Extract assigner from argsUniform 2024-05-16 12:14:03 +02:00
Ben 9717591224 Updates bot config to use ids instead of names 2024-05-16 11:54:31 +02:00
Ben 64ae7c8efe Latest image of Codex 2024-05-15 10:34:40 +02:00
benbierens d8d7605ce5 Increases marketplace graceperiod timespan. 2024-05-10 10:08:45 +02:00
benbierens 6995cbfb23 hacky way to download container logs while retries are failing. 2024-05-09 09:38:04 +02:00
benbierens c6a757d6fb Rename AddCodex. Better retry logging 2024-05-09 09:32:48 +02:00
benbierens c01f9dbb21 Fixes crash in blocktimefinder. 2024-05-09 08:57:19 +02:00
Ben 0ec43a9325 Shuts up json-serialization codex logging 2024-05-07 11:04:32 +02:00
Ben 266c661958 Updates codex API 2024-05-07 10:21:05 +02:00
Ben 8ea4c4ee37 Clean up codex long tests 2024-05-07 10:15:02 +02:00
Ben 725dfc23a6 Cleanup of codex tests 2024-05-07 09:49:00 +02:00
Ben 39ff757b39 Update + instructions for codex contracts 2024-05-06 16:12:26 +02:00
benbierens e187bfc941 Changes time.retry to fixed timelength instead of fixed number of retries 2024-05-02 08:41:20 +02:00
benbierens dd36929a81 Sets scalability tests to current codex image 2024-05-01 13:51:46 +02:00
Ben 2902f6baab latest master image of Codex 2024-05-01 10:05:46 +02:00
benbierens f38f7861a0 Log comparing test 2024-04-27 18:08:54 +02:00
benbierens 84a68521b0 sets up parser for codex log lines 2024-04-27 14:20:31 +02:00
benbierens ce7ba47a32 trying to detect upload slowdown related to number of blocks in node 2024-04-27 11:33:13 +02:00
Ben a20e9a30cf bumps to image with proof support 2024-04-26 11:10:14 +02:00
Ben 52cec0e9f3 bump codex image 2024-04-26 10:43:36 +02:00
Ben 9a6866cf8e clean up logs in Codex 2024-04-25 15:04:02 +02:00
Ben 15d7e6483e Allows parsing longs only from a certain timestamp 2024-04-25 10:57:19 +02:00
Ben e1c710a093 Merge branch 'less-announce-tests' 2024-04-25 10:25:29 +02:00
Ben 0c700ded9d Test for detecting common log lines 2024-04-25 10:25:12 +02:00
49 changed files with 638 additions and 675 deletions
+33 -158
View File
@@ -4,9 +4,8 @@ namespace ArgsUniform
{
public class ArgsUniform<T>
{
private readonly Assigner<T> assigner;
private readonly Action printAppInfo;
private readonly object? defaultsProvider;
private readonly IEnv.IEnv env;
private readonly string[] args;
private const int cliStart = 8;
private const int shortStart = 38;
@@ -31,9 +30,9 @@ namespace ArgsUniform
public ArgsUniform(Action printAppInfo, object defaultsProvider, IEnv.IEnv env, params string[] args)
{
this.printAppInfo = printAppInfo;
this.defaultsProvider = defaultsProvider;
this.env = env;
this.args = args;
assigner = new Assigner<T>(env, args, defaultsProvider);
}
public T Parse(bool printResult = false)
@@ -42,7 +41,7 @@ namespace ArgsUniform
{
printAppInfo();
PrintHelp();
throw new Exception();
Environment.Exit(0);
}
var result = Activator.CreateInstance<T>();
@@ -53,18 +52,16 @@ namespace ArgsUniform
var attr = uniformProperty.GetCustomAttribute<UniformAttribute>();
if (attr != null)
{
if (!UniformAssign(result, attr, uniformProperty) && attr.Required)
if (!assigner.UniformAssign(result, attr, uniformProperty) && attr.Required)
{
{
missingRequired.Add(uniformProperty);
}
missingRequired.Add(uniformProperty);
}
}
}
if (missingRequired.Any())
{
PrintResults(result, uniformProperties);
PrintResults(printResult,result, uniformProperties);
Print("");
foreach (var missing in missingRequired)
{
@@ -75,37 +72,39 @@ namespace ArgsUniform
}
PrintHelp();
throw new ArgumentException("Unable to assemble all required arguments");
Environment.Exit(1);
}
if (printResult)
{
PrintResults(result, uniformProperties);
}
PrintResults(printResult, result, uniformProperties);
return result;
}
private void PrintResults(T result, PropertyInfo[] uniformProperties)
{
Print("");
foreach (var p in uniformProperties)
{
Print($"\t{p.Name} = {p.GetValue(result)}");
}
Print("");
}
public void PrintHelp()
{
Print("");
PrintAligned("CLI option:", "(short)", "Environment variable:", "Description");
var attrs = typeof(T).GetProperties().Where(m => m.GetCustomAttributes(typeof(UniformAttribute), false).Length == 1).Select(p => p.GetCustomAttribute<UniformAttribute>()).Where(a => a != null).ToArray();
foreach (var attr in attrs)
PrintAligned("CLI option:", "(short)", "Environment variable:", "Description", "(default)");
var props = typeof(T).GetProperties().Where(m => m.GetCustomAttributes(typeof(UniformAttribute), false).Length == 1).ToArray();
foreach (var prop in props)
{
var a = attr!;
var optional = !a.Required ? " *" : "";
PrintAligned($"--{a.Arg}=...", $"({a.ArgShort})", a.EnvVar, a.Description + optional);
var a = prop.GetCustomAttribute<UniformAttribute>();
if (a != null)
{
var optional = !a.Required ? " (optional)" : "";
var def = assigner.DescribeDefaultFor(prop);
PrintAligned($"--{a.Arg}=...", $"({a.ArgShort})", a.EnvVar, a.Description + optional, $"({def})");
}
}
Print("");
}
private void PrintResults(bool printResult, T result, PropertyInfo[] uniformProperties)
{
if (!printResult) return;
Print("");
foreach (var p in uniformProperties)
{
Print($"\t{p.Name} = {p.GetValue(result)}");
}
Print("");
}
@@ -115,7 +114,7 @@ namespace ArgsUniform
Console.WriteLine(msg);
}
private void PrintAligned(string cli, string s, string env, string desc)
private void PrintAligned(string cli, string s, string env, string desc, string def)
{
Console.CursorLeft = cliStart;
Console.Write(cli);
@@ -124,132 +123,8 @@ namespace ArgsUniform
Console.CursorLeft = envStart;
Console.Write(env);
Console.CursorLeft = descStart;
Console.Write(desc + Environment.NewLine);
}
private object GetDefaultValue(Type t)
{
if (t.IsValueType) return Activator.CreateInstance(t)!;
return null!;
}
private bool UniformAssign(T result, UniformAttribute attr, PropertyInfo uniformProperty)
{
if (AssignFromArgsIfAble(result, attr, uniformProperty)) return true;
if (AssignFromEnvVarIfAble(result, attr, uniformProperty)) return true;
if (AssignFromDefaultsIfAble(result, uniformProperty)) return true;
return false;
}
private bool AssignFromDefaultsIfAble(T result, PropertyInfo uniformProperty)
{
var currentValue = uniformProperty.GetValue(result);
var isEmptryString = (currentValue as string) == string.Empty;
if (currentValue != GetDefaultValue(uniformProperty.PropertyType) && !isEmptryString) return true;
if (defaultsProvider == null) return false;
var defaultProperty = defaultsProvider.GetType().GetProperties().SingleOrDefault(p => p.Name == uniformProperty.Name);
if (defaultProperty == null) return false;
var value = defaultProperty.GetValue(defaultsProvider);
if (value != null)
{
return Assign(result, uniformProperty, value);
}
return false;
}
private bool AssignFromEnvVarIfAble(T result, UniformAttribute attr, PropertyInfo uniformProperty)
{
var e = env.GetEnvVarOrDefault(attr.EnvVar, string.Empty);
if (!string.IsNullOrEmpty(e))
{
return Assign(result, uniformProperty, e);
}
return false;
}
private bool AssignFromArgsIfAble(T result, UniformAttribute attr, PropertyInfo uniformProperty)
{
var fromArg = GetFromArgs(attr.Arg);
if (fromArg != null)
{
return Assign(result, uniformProperty, fromArg);
}
var fromShort = GetFromArgs(attr.ArgShort);
if (fromShort != null)
{
return Assign(result, uniformProperty, fromShort);
}
return false;
}
private bool Assign(T result, PropertyInfo uniformProperty, object value)
{
if (uniformProperty.PropertyType == value.GetType())
{
uniformProperty.SetValue(result, value);
return true;
}
else
{
if (uniformProperty.PropertyType == typeof(string) || uniformProperty.PropertyType == typeof(int))
{
uniformProperty.SetValue(result, Convert.ChangeType(value, uniformProperty.PropertyType));
return true;
}
else
{
if (uniformProperty.PropertyType == typeof(int?)) return AssignOptionalInt(result, uniformProperty, value);
if (uniformProperty.PropertyType.IsEnum) return AssignEnum(result, uniformProperty, value);
if (uniformProperty.PropertyType == typeof(bool)) return AssignBool(result, uniformProperty, value);
throw new NotSupportedException();
}
}
}
private static bool AssignEnum(T result, PropertyInfo uniformProperty, object value)
{
var s = value.ToString();
if (Enum.TryParse(uniformProperty.PropertyType, s, out var e))
{
uniformProperty.SetValue(result, e);
return true;
}
return false;
}
private static bool AssignOptionalInt(T result, PropertyInfo uniformProperty, object value)
{
if (int.TryParse(value.ToString(), out int i))
{
uniformProperty.SetValue(result, i);
return true;
}
return false;
}
private static bool AssignBool(T result, PropertyInfo uniformProperty, object value)
{
var s = value.ToString();
if (s == "1" || (s != null && s.ToLowerInvariant() == "true"))
{
uniformProperty.SetValue(result, true);
}
return true;
}
private string? GetFromArgs(string key)
{
var argKey = $"--{key}=";
var arg = args.FirstOrDefault(a => a.StartsWith(argKey));
if (arg != null)
{
return arg.Substring(argKey.Length);
}
return null;
Console.Write(desc + " ");
Console.Write(def + Environment.NewLine);
}
}
}
+160
View File
@@ -0,0 +1,160 @@
using System.Reflection;
namespace ArgsUniform
{
public class Assigner<T>
{
private readonly IEnv.IEnv env;
private readonly string[] args;
private readonly object? defaultsProvider;
public Assigner(IEnv.IEnv env, string[] args, object? defaultsProvider)
{
this.env = env;
this.args = args;
this.defaultsProvider = defaultsProvider;
}
public bool UniformAssign(T result, UniformAttribute attr, PropertyInfo uniformProperty)
{
if (AssignFromArgsIfAble(result, attr, uniformProperty)) return true;
if (AssignFromEnvVarIfAble(result, attr, uniformProperty)) return true;
if (AssignFromDefaultsIfAble(result, uniformProperty)) return true;
return false;
}
public string DescribeDefaultFor(PropertyInfo property)
{
var obj = Activator.CreateInstance<T>();
var defaultValue = GetDefaultValue(obj, property);
if (defaultValue == null) return "";
if (defaultValue is string str)
{
return "\"" + str + "\"";
}
return defaultValue.ToString() ?? string.Empty;
}
private object? GetDefaultValue(T result, PropertyInfo uniformProperty)
{
// Get value from object's static initializer if it's there.
var currentValue = uniformProperty.GetValue(result);
if (currentValue != null) return currentValue;
// Get value from defaults-provider object if it's there.
if (defaultsProvider == null) return null;
var defaultProperty = defaultsProvider.GetType().GetProperties().SingleOrDefault(p => p.Name == uniformProperty.Name);
if (defaultProperty == null) return null;
return defaultProperty.GetValue(defaultsProvider);
}
private bool AssignFromDefaultsIfAble(T result, PropertyInfo uniformProperty)
{
var defaultValue = GetDefaultValue(result, uniformProperty);
var isEmptryString = (defaultValue as string) == string.Empty;
if (defaultValue != null && defaultValue != GetDefaultValueForType(uniformProperty.PropertyType) && !isEmptryString)
{
return Assign(result, uniformProperty, defaultValue);
}
return false;
}
private bool AssignFromEnvVarIfAble(T result, UniformAttribute attr, PropertyInfo uniformProperty)
{
var e = env.GetEnvVarOrDefault(attr.EnvVar, string.Empty);
if (!string.IsNullOrEmpty(e))
{
return Assign(result, uniformProperty, e);
}
return false;
}
private bool AssignFromArgsIfAble(T result, UniformAttribute attr, PropertyInfo uniformProperty)
{
var fromArg = GetFromArgs(attr.Arg);
if (fromArg != null)
{
return Assign(result, uniformProperty, fromArg);
}
var fromShort = GetFromArgs(attr.ArgShort);
if (fromShort != null)
{
return Assign(result, uniformProperty, fromShort);
}
return false;
}
private bool Assign(T result, PropertyInfo uniformProperty, object value)
{
if (uniformProperty.PropertyType == value.GetType())
{
uniformProperty.SetValue(result, value);
return true;
}
else
{
if (uniformProperty.PropertyType == typeof(string) || uniformProperty.PropertyType == typeof(int))
{
uniformProperty.SetValue(result, Convert.ChangeType(value, uniformProperty.PropertyType));
return true;
}
else
{
if (uniformProperty.PropertyType == typeof(int?)) return AssignOptionalInt(result, uniformProperty, value);
if (uniformProperty.PropertyType.IsEnum) return AssignEnum(result, uniformProperty, value);
if (uniformProperty.PropertyType == typeof(bool)) return AssignBool(result, uniformProperty, value);
throw new NotSupportedException();
}
}
}
private static bool AssignEnum(T result, PropertyInfo uniformProperty, object value)
{
var s = value.ToString();
if (Enum.TryParse(uniformProperty.PropertyType, s, out var e))
{
uniformProperty.SetValue(result, e);
return true;
}
return false;
}
private static bool AssignOptionalInt(T result, PropertyInfo uniformProperty, object value)
{
if (int.TryParse(value.ToString(), out int i))
{
uniformProperty.SetValue(result, i);
return true;
}
return false;
}
private static bool AssignBool(T result, PropertyInfo uniformProperty, object value)
{
var s = value.ToString();
if (s == "1" || (s != null && s.ToLowerInvariant() == "true"))
{
uniformProperty.SetValue(result, true);
}
return true;
}
private string? GetFromArgs(string key)
{
var argKey = $"--{key}=";
var arg = args.FirstOrDefault(a => a.StartsWith(argKey));
if (arg != null)
{
return arg.Substring(argKey.Length);
}
return null;
}
private static object GetDefaultValueForType(Type t)
{
if (t.IsValueType) return Activator.CreateInstance(t)!;
return null!;
}
}
}
+1 -1
View File
@@ -58,7 +58,7 @@ namespace Core
{
lock (httpLock)
{
return Time.Retry(operation, timeSet.HttpMaxNumberOfRetries(), timeSet.HttpCallRetryDelay(), description);
return Time.Retry(operation, timeSet.HttpRetryTimeout(), timeSet.HttpCallRetryDelay(), description);
}
}
+28 -7
View File
@@ -2,10 +2,31 @@
{
public interface ITimeSet
{
/// <summary>
/// Timeout for a single HTTP call.
/// </summary>
TimeSpan HttpCallTimeout();
int HttpMaxNumberOfRetries();
/// <summary>
/// Maximum total time to attempt to make a successful HTTP call to a service.
/// When HTTP calls time out during this timespan, retries will be made.
/// </summary>
TimeSpan HttpRetryTimeout();
/// <summary>
/// After a failed HTTP call, wait this long before trying again.
/// </summary>
TimeSpan HttpCallRetryDelay();
/// <summary>
/// After a failed K8s operation, wait this long before trying again.
/// </summary>
TimeSpan K8sOperationRetryDelay();
/// <summary>
/// Maximum total time to attempt to perform a successful k8s operation.
/// If k8s operations fail during this timespan, retries will be made.
/// </summary>
TimeSpan K8sOperationTimeout();
}
@@ -16,9 +37,9 @@
return TimeSpan.FromMinutes(3);
}
public int HttpMaxNumberOfRetries()
public TimeSpan HttpRetryTimeout()
{
return 3;
return TimeSpan.FromMinutes(10);
}
public TimeSpan HttpCallRetryDelay()
@@ -41,17 +62,17 @@
{
public TimeSpan HttpCallTimeout()
{
return TimeSpan.FromHours(2);
return TimeSpan.FromMinutes(30);
}
public int HttpMaxNumberOfRetries()
public TimeSpan HttpRetryTimeout()
{
return 1;
return TimeSpan.FromHours(2.2);
}
public TimeSpan HttpCallRetryDelay()
{
return TimeSpan.FromSeconds(2);
return TimeSpan.FromSeconds(20);
}
public TimeSpan K8sOperationRetryDelay()
+1 -1
View File
@@ -65,7 +65,7 @@ namespace FileUtils
if (readExpected == 0 && readActual == 0)
{
log.Log($"OK: '{Describe()}' is equal to '{actual.Describe()}'.");
log.Log($"OK: {Describe()} is equal to {actual.Describe()}.");
return;
}
@@ -712,7 +712,7 @@ namespace KubernetesWorkflow
{
return Time.Retry(() => GetPodForDeplomentInternal(deployment),
// We will wait up to 1 minute, k8s might be moving pods around.
maxRetries: 6,
maxTimeout: TimeSpan.FromMinutes(1),
retryTime: TimeSpan.FromSeconds(10),
description: "Find pod by label for deployment.");
}
@@ -20,7 +20,7 @@ namespace KubernetesWorkflow.Types
[JsonIgnore]
public string Name
{
get { return $"{Containers.Length}x '{Containers.First().Name}'"; }
get { return $"'{string.Join("&", Containers.Select(c => c.Name).ToArray())}'"; }
}
public string Describe()
@@ -20,6 +20,7 @@ namespace NethereumWorkflow.BlockUtils
public BlockTimeEntry Get(ulong blockNumber)
{
bounds.Initialize();
var b = cache.Get(blockNumber);
if (b != null) return b;
return GetBlock(blockNumber);
+44 -21
View File
@@ -1,4 +1,6 @@
namespace Utils
using System.Diagnostics;
namespace Utils
{
public static class Time
{
@@ -83,37 +85,40 @@
public static void Retry(Action action, string description)
{
Retry(action, 1, description);
Retry(action, TimeSpan.FromSeconds(30), description);
}
public static T Retry<T>(Func<T> action, string description)
{
return Retry(action, 1, description);
return Retry(action, TimeSpan.FromSeconds(30), description);
}
public static void Retry(Action action, int maxRetries, string description)
public static void Retry(Action action, TimeSpan maxTimeout, string description)
{
Retry(action, maxRetries, TimeSpan.FromSeconds(5), description);
Retry(action, maxTimeout, TimeSpan.FromSeconds(5), description);
}
public static T Retry<T>(Func<T> action, int maxRetries, string description)
public static T Retry<T>(Func<T> action, TimeSpan maxTimeout, string description)
{
return Retry(action, maxRetries, TimeSpan.FromSeconds(5), description);
return Retry(action, maxTimeout, TimeSpan.FromSeconds(5), description);
}
public static void Retry(Action action, int maxRetries, TimeSpan retryTime, string description)
public static void Retry(Action action, TimeSpan maxTimeout, TimeSpan retryTime, string description)
{
var start = DateTime.UtcNow;
var retries = 0;
var exceptions = new List<Exception>();
var tries = 1;
var tryInfo = new List<(Exception, TimeSpan)>();
while (true)
{
if (retries > maxRetries)
var duration = DateTime.UtcNow - start;
if (duration > maxTimeout)
{
var duration = DateTime.UtcNow - start;
throw new TimeoutException($"Retry '{description}' timed out after {maxRetries} tries over {Time.FormatDuration(duration)}.", new AggregateException(exceptions));
var info = FormatTryInfos(tryInfo);
throw new TimeoutException($"Retry '{description}' timed out after {tries} tries over {FormatDuration(duration)}.{Environment.NewLine}{info}");
}
var sw = Stopwatch.StartNew();
try
{
action();
@@ -121,25 +126,42 @@
}
catch (Exception ex)
{
exceptions.Add(ex);
retries++;
tryInfo.Add((ex, sw.Elapsed));
tries++;
}
Sleep(retryTime);
}
}
public static T Retry<T>(Func<T> action, int maxRetries, TimeSpan retryTime, string description)
private static string FormatTryInfos(List<(Exception, TimeSpan)> tryInfo)
{
return string.Join(Environment.NewLine, tryInfo.Select(FormatTryInfo).ToArray());
}
private static string FormatTryInfo((Exception, TimeSpan) info, int index)
{
return $"Attempt {index} took {FormatDuration(info.Item2)} and failed with exception {info.Item1}.";
}
private static Action<int> failedCallback = i => { };
public static void SetRetryFailedCallback(Action<int> onRetryFailed)
{
failedCallback = onRetryFailed;
}
public static T Retry<T>(Func<T> action, TimeSpan maxTimeout, TimeSpan retryTime, string description)
{
var start = DateTime.UtcNow;
var retries = 0;
var tries = 1;
var exceptions = new List<Exception>();
while (true)
{
if (retries > maxRetries)
var duration = DateTime.UtcNow - start;
if (duration > maxTimeout)
{
var duration = DateTime.UtcNow - start;
throw new TimeoutException($"Retry '{description}' timed out after {maxRetries} tries over {Time.FormatDuration(duration)}.", new AggregateException(exceptions));
throw new TimeoutException($"Retry '{description}' timed out after {tries} tries over {FormatDuration(duration)}.", new AggregateException(exceptions));
}
try
@@ -149,7 +171,8 @@
catch (Exception ex)
{
exceptions.Add(ex);
retries++;
failedCallback(tries);
tries++;
}
Sleep(retryTime);
File diff suppressed because one or more lines are too long
@@ -1 +1,14 @@
This code was generated using the Nethereum code generator, here: http://playground.nethereum.com
1. Go to site -> Abi Code Gen.
1. Contract name = "Marketplace".
1. In container, get "/hardhat/artifacts/contracts/Marketplace.sol/Marketplace.json".
1. Save only ABI section as new JSON. (top-level is a json array.)
1. From original JSON get byte code.
1. Put ABI JSON and byte code into site.
1. Generate.
1. From site generated code, copy `public partial class MarketplaceDeployment` and everything after it. (be considerate of namespace brackets!)
1. In Marketplace/Marketplace.cs, replace content of 'namespace CodexContractsPlugin.Marketplace'.
+1 -1
View File
@@ -9,7 +9,7 @@ namespace CodexPlugin
public class ApiChecker
{
// <INSERT-OPENAPI-YAML-HASH>
private const string OpenApiYamlHash = "5A-B0-2A-AC-42-B1-A2-49-6F-9D-4E-D8-56-40-10-A6-67-F4-0D-2A-9F-E0-84-5C-EB-B8-2D-4F-D8-56-79-6C";
private const string OpenApiYamlHash = "0F-C8-02-1E-2C-2C-15-F6-91-6A-01-31-11-49-95-06-79-26-25-BF-27-3C-A8-2E-5F-7F-34-FD-C0-57-A0-9A";
private const string OpenApiFilePath = "/codex/openapi.yaml";
private const string DisableEnvironmentVariable = "CODEXPLUGIN_DISABLE_APICHECK";
@@ -7,7 +7,8 @@ namespace CodexPlugin
{
public class CodexContainerRecipe : ContainerRecipeFactory
{
private const string DefaultDockerImage = "codexstorage/nim-codex:sha-1524803-dist-tests";
private const string DefaultDockerImage = "codexstorage/nim-codex:sha-267266a-dist-tests";
public const string ApiPortTag = "codex_api_port";
public const string ListenPortTag = "codex_listen_port";
public const string MetricsPortTag = "codex_metrics_port";
+105
View File
@@ -0,0 +1,105 @@
using System.Globalization;
namespace CodexPlugin
{
public class CodexLogLine
{
public static CodexLogLine? Parse(string line)
{
try
{
if (string.IsNullOrEmpty(line) ||
line.Length < 34 ||
line[3] != ' ' ||
line[33] != ' ') return null;
line = line.Replace(Environment.NewLine, string.Empty);
var level = line.Substring(0, 3);
var dtLine = line.Substring(4, 23);
var firstEqualSign = line.IndexOf('=');
var msgStart = 34;
var msgEnd = line.Substring(0, firstEqualSign).LastIndexOf(' ');
var msg = line.Substring(msgStart, msgEnd - msgStart).Trim();
var attrsLine = line.Substring(msgEnd);
var attrs = SplitAttrs(attrsLine);
var format = "yyyy-MM-dd HH:mm:ss.fff";
var dt = DateTime.ParseExact(dtLine, format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToUniversalTime();
return new CodexLogLine()
{
LogLevel = level,
TimestampUtc = dt,
Message = msg,
Attributes = attrs
};
}
catch
{
return null;
}
}
public string LogLevel { get; set; } = string.Empty;
public DateTime TimestampUtc { get; set; }
public string Message { get; set; } = string.Empty;
public Dictionary<string, string> Attributes { get; private set; } = new Dictionary<string, string>();
/// <summary>
/// After too much time spent cursing at regexes, here's what I got:
/// Parses input string into 'key=value' pair, considerate of quoted (") values.
/// </summary>
private static Dictionary<string, string> SplitAttrs(string input)
{
input += " ";
var result = new Dictionary<string, string>();
var key = string.Empty;
var value = string.Empty;
var mode = 1;
var inQuote = false;
foreach (var c in input)
{
if (mode == 1)
{
if (c == '=') mode = 2;
else if (c == ' ')
{
if (string.IsNullOrEmpty(key)) continue;
else
{
result.Add(key, string.Empty);
key = string.Empty;
value = string.Empty;
}
}
else key += c;
}
else if (mode == 2)
{
if (c == ' ' && !inQuote)
{
result.Add(key, value);
key = string.Empty;
value = string.Empty;
mode = 1;
}
else if (c == '\"')
{
inQuote = !inQuote;
}
else
{
value += c;
}
}
}
return result;
}
}
}
+1
View File
@@ -52,6 +52,7 @@ namespace CodexPlugin
public CodexLogLevel Libp2p { get; set; }
public CodexLogLevel ContractClock { get; set; } = CodexLogLevel.Warn;
public CodexLogLevel? BlockExchange { get; }
public CodexLogLevel JsonSerialize { get; set; } = CodexLogLevel.Warn;
}
public class CodexSetup : CodexStartupConfig, ICodexSetup
@@ -73,11 +73,18 @@ namespace CodexPlugin
"contracts",
"clock"
};
var jsonSerializeTopics = new[]
{
"serde",
"json",
"serialization"
};
level = $"{level};" +
$"{CustomTopics.DiscV5.ToString()!.ToLowerInvariant()}:{string.Join(",", discV5Topics)};" +
$"{CustomTopics.Libp2p.ToString()!.ToLowerInvariant()}:{string.Join(",", libp2pTopics)};" +
$"{CustomTopics.ContractClock.ToString().ToLowerInvariant()}:{string.Join(",", contractClockTopics)}";
$"{CustomTopics.ContractClock.ToString().ToLowerInvariant()}:{string.Join(",", contractClockTopics)};" +
$"{CustomTopics.JsonSerialize.ToString().ToLowerInvariant()}:{string.Join(",", jsonSerializeTopics)}";
if (CustomTopics.BlockExchange != null)
{
+1 -1
View File
@@ -56,7 +56,7 @@ namespace CodexPlugin
ProofProbability = ToDecInt(purchase.ProofProbability),
Reward = ToDecInt(purchase.PricePerSlotPerSecond),
Collateral = ToDecInt(purchase.RequiredCollateral),
Expiry = ToDecInt(DateTimeOffset.UtcNow.ToUnixTimeSeconds() + purchase.Expiry.TotalSeconds),
Expiry = ToDecInt(purchase.Expiry.TotalSeconds),
Nodes = Convert.ToInt32(purchase.MinRequiredNumberOfNodes),
Tolerance = Convert.ToInt32(purchase.NodeFailureTolerance)
};
@@ -83,7 +83,7 @@ namespace CodexPlugin
{
private readonly ILog log;
private readonly CodexAccess codexAccess;
private readonly TimeSpan gracePeriod = TimeSpan.FromSeconds(10);
private readonly TimeSpan gracePeriod = TimeSpan.FromSeconds(30);
private DateTime? contractStartUtc;
public StoragePurchaseContract(ILog log, CodexAccess codexAccess, string purchaseId, StoragePurchaseRequest purchase)
+1 -2
View File
@@ -213,8 +213,7 @@ components:
description: Number as decimal string that represents how much collateral is asked from hosts that wants to fill a slots
expiry:
type: string
description: Number as decimal string that represents expiry time of the request (in unix timestamp)
description: Number as decimal string that represents expiry threshold in seconds from when the Request is submitted. When the threshold is reached and the Request does not find requested amount of nodes to host the data, the Request is voided. The number of seconds can not be higher then the Request's duration itself.
StorageAsk:
type: object
required:
@@ -1,3 +1,4 @@
using CodexPlugin;
using CodexTests;
using DistTestCore;
using FileUtils;
@@ -7,35 +8,45 @@ using Utils;
namespace CodexLongTests.BasicTests
{
[TestFixture]
public class DownloadTests : CodexDistTest
public class DownloadTests : AutoBootstrapDistTest
{
[TestCase(3, 500)]
[TestCase(5, 100)]
[TestCase(10, 256)]
[Test]
[Combinatorial]
[UseLongTimeouts]
public void ParallelDownload(int numberOfNodes, int filesizeMb)
public void ParallelDownload(
[Values(1, 3, 5)] int numberOfFiles,
[Values(10, 50, 100)] int filesizeMb)
{
var group = AddCodex(numberOfNodes);
var host = AddCodex();
var host = StartCodex();
var client = StartCodex();
foreach (var node in group)
var testfiles = new List<TrackedFile>();
var contentIds = new List<ContentId>();
var downloadedFiles = new List<TrackedFile?>();
for (int i = 0; i < numberOfFiles; i++)
{
host.ConnectToPeer(node);
testfiles.Add(GenerateTestFile(filesizeMb.MB()));
contentIds.Add(new ContentId());
downloadedFiles.Add(null);
}
var testFile = GenerateTestFile(filesizeMb.MB());
var contentId = host.UploadFile(testFile);
var list = new List<Task<TrackedFile?>>();
foreach (var node in group)
for (int i = 0; i < numberOfFiles; i++)
{
list.Add(Task.Run(() => { return node.DownloadContent(contentId); }));
contentIds[i] = host.UploadFile(testfiles[i]);
}
Task.WaitAll(list.ToArray());
foreach (var task in list)
var downloadTasks = new List<Task>();
for (int i = 0; i < numberOfFiles; i++)
{
testFile.AssertIsEqual(task.Result);
downloadTasks.Add(Task.Run(() => { downloadedFiles[i] = client.DownloadContent(contentIds[i]); }));
}
Task.WaitAll(downloadTasks.ToArray());
for (int i = 0; i < numberOfFiles; i++)
{
testfiles[i].AssertIsEqual(downloadedFiles[i]);
}
}
}
@@ -48,7 +48,9 @@ namespace CodexLongTests.BasicTests
var expectedFile = GenerateTestFile(sizeMB);
var node = AddCodex(s => s.WithStorageQuota((size + 10).MB()));
var node = StartCodex(s => s.WithStorageQuota((size + 10).MB()));
Time.SetRetryFailedCallback(i => OnFailed(i, node));
var uploadStart = DateTime.UtcNow;
var cid = node.UploadFile(expectedFile);
@@ -60,6 +62,17 @@ namespace CodexLongTests.BasicTests
AssertTimeConstraint(uploadStart, downloadStart, downloadFinished, size);
}
private void OnFailed(int tries, ICodexNode node)
{
if (tries < 5) return;
if (tries % 10 == 0)
{
Log($"After try {tries}, downloading node log.");
Ci.DownloadLog(node);
}
}
private void AssertTimeConstraint(DateTime uploadStart, DateTime downloadStart, DateTime downloadFinished, long size)
{
float sizeInMB = size;
@@ -6,10 +6,12 @@ namespace CodexLongTests.BasicTests
{
public class TestInfraTests : CodexDistTest
{
[Test, UseLongTimeouts]
[Test]
[UseLongTimeouts]
[Ignore("Not supported atm")]
public void TestInfraShouldHave1000AddressSpacesPerPod()
{
var group = AddCodex(1000, s => s.EnableMetrics());
var group = StartCodex(1000, s => s.EnableMetrics());
var nodeIds = group.Select(n => n.GetDebugInfo().Id).ToArray();
@@ -17,12 +19,14 @@ namespace CodexLongTests.BasicTests
"Not all created nodes provided a unique id.");
}
[Test, UseLongTimeouts]
[Test]
[UseLongTimeouts]
[Ignore("Not supported atm")]
public void TestInfraSupportsManyConcurrentPods()
{
for (var i = 0; i < 20; i++)
{
var n = AddCodex();
var n = StartCodex();
Assert.That(!string.IsNullOrEmpty(n.GetDebugInfo().Id));
}
+21 -23
View File
@@ -8,41 +8,39 @@ using Utils;
namespace CodexLongTests.BasicTests
{
[TestFixture]
public class UploadTests : CodexDistTest
public class UploadTests : AutoBootstrapDistTest
{
[TestCase(3, 50)]
[TestCase(5, 75)]
[TestCase(10, 25)]
[Test]
[Combinatorial]
[UseLongTimeouts]
public void ParallelUpload(int numberOfNodes, int filesizeMb)
public void ParallelUpload(
[Values(1, 3, 5)] int numberOfFiles,
[Values(10, 50, 100)] int filesizeMb)
{
var group = AddCodex(numberOfNodes);
var host = AddCodex();
foreach (var node in group)
{
host.ConnectToPeer(node);
}
var host = StartCodex();
var client = StartCodex();
var testfiles = new List<TrackedFile>();
var contentIds = new List<Task<ContentId>>();
var contentIds = new List<ContentId>();
for (int i = 0; i < group.Count(); i++)
for (int i = 0; i < numberOfFiles; i++)
{
testfiles.Add(GenerateTestFile(filesizeMb.MB()));
var n = i;
contentIds.Add(Task.Run(() => { return host.UploadFile(testfiles[n]); }));
contentIds.Add(new ContentId());
}
var downloads = new List<Task<TrackedFile?>>();
for (int i = 0; i < group.Count(); i++)
var uploadTasks = new List<Task>();
for (int i = 0; i < numberOfFiles; i++)
{
var n = i;
downloads.Add(Task.Run(() => { return group[n].DownloadContent(contentIds[n].Result); }));
uploadTasks.Add(Task.Run(() => { contentIds[i] = host.UploadFile(testfiles[i]); }));
}
Task.WaitAll(downloads.ToArray());
for (int i = 0; i < group.Count(); i++)
Task.WaitAll(uploadTasks.ToArray());
for (int i = 0; i < numberOfFiles; i++)
{
testfiles[i].AssertIsEqual(downloads[i].Result);
var downloaded = client.DownloadContent(contentIds[i]);
testfiles[i].AssertIsEqual(downloaded);
}
}
}
@@ -15,7 +15,7 @@ namespace CodexLongTests.DownloadConnectivityTests
[Values(10, 15, 20)] int numberOfNodes,
[Values(10, 100)] int sizeMBs)
{
for (var i = 0; i < numberOfNodes; i++) AddCodex();
for (var i = 0; i < numberOfNodes; i++) StartCodex();
CreatePeerDownloadTestHelpers().AssertFullDownloadInterconnectivity(GetAllOnlineCodexNodes(), sizeMBs.MB());
}
@@ -16,7 +16,7 @@ namespace CodexTests.ScalabilityTests
[Values(100, 1000)] int fileSize
)
{
var hosts = AddCodex(numberOfHosts, s => s.WithLogLevel(CodexPlugin.CodexLogLevel.Trace));
var hosts = StartCodex(numberOfHosts, s => s.WithLogLevel(CodexPlugin.CodexLogLevel.Trace));
var file = GenerateTestFile(fileSize.MB());
var cid = hosts[0].UploadFile(file);
var tailOfManifestCid = cid.Id.Substring(cid.Id.Length - 6);
@@ -38,7 +38,7 @@ namespace CodexTests.ScalabilityTests
foreach (var h in hosts) h.DownloadContent(cid);
var client = AddCodex(s => s.WithLogLevel(CodexPlugin.CodexLogLevel.Trace));
var client = StartCodex(s => s.WithLogLevel(CodexPlugin.CodexLogLevel.Trace));
var resultFile = client.DownloadContent(cid);
resultFile!.AssertIsEqual(file);
@@ -9,9 +9,6 @@ namespace CodexTests.ScalabilityTests;
[TestFixture]
public class ScalabilityTests : CodexDistTest
{
private const string PatchedImage = "codexstorage/nim-codex:sha-9aeac06-dist-tests";
private const string MasterImage = "codexstorage/nim-codex:sha-5380912-dist-tests";
/// <summary>
/// We upload a file to node A, then download it with B.
/// Then we stop node A, and download again with node C.
@@ -22,16 +19,13 @@ public class ScalabilityTests : CodexDistTest
[DontDownloadLogs]
public void ShouldMaintainFileInNetwork(
[Values(10, 40, 80, 100)] int numberOfNodes,
[Values(100, 1000, 5000, 10000)] int fileSizeInMb,
[Values(true, false)] bool usePatchedImage
[Values(100, 1000, 5000, 10000)] int fileSizeInMb
)
{
CodexContainerRecipe.DockerImageOverride = usePatchedImage ? PatchedImage : MasterImage;
var logLevel = CodexLogLevel.Info;
var bootstrap = AddCodex(s => s.WithLogLevel(logLevel));
var nodes = AddCodex(numberOfNodes - 1, s => s
var bootstrap = StartCodex(s => s.WithLogLevel(logLevel));
var nodes = StartCodex(numberOfNodes - 1, s => s
.WithBootstrapNode(bootstrap)
.WithLogLevel(logLevel)
.WithStorageQuota((fileSizeInMb + 50).MB())
@@ -58,23 +52,20 @@ public class ScalabilityTests : CodexDistTest
/// We upload a file to each node, to put a more wide-spread load on the network.
/// Then we run the same test as ShouldMaintainFileInNetwork.
/// </summary>
[Ignore("Make ShouldMaintainFileInNetwork pass reliably first.")]
[Ignore("Fix ShouldMaintainFileInNetwork for all values first")]
[Test]
[Combinatorial]
[UseLongTimeouts]
[DontDownloadLogs]
public void EveryoneGetsAFile(
[Values(10, 40, 80, 100)] int numberOfNodes,
[Values(100, 1000)] int fileSizeInMb,
[Values(true, false)] bool usePatchedImage
[Values(100, 1000, 5000, 10000)] int fileSizeInMb
)
{
CodexContainerRecipe.DockerImageOverride = usePatchedImage ? PatchedImage : MasterImage;
var logLevel = CodexLogLevel.Info;
var bootstrap = AddCodex(s => s.WithLogLevel(logLevel));
var nodes = AddCodex(numberOfNodes - 1, s => s
var bootstrap = StartCodex(s => s.WithLogLevel(logLevel));
var nodes = StartCodex(numberOfNodes - 1, s => s
.WithBootstrapNode(bootstrap)
.WithLogLevel(logLevel)
.WithStorageQuota((fileSizeInMb + 50).MB())
+1 -1
View File
@@ -8,7 +8,7 @@ namespace CodexTests
[SetUp]
public void SetUpBootstrapNode()
{
BootstrapNode = AddCodex(s => s.WithName("BOOTSTRAP"));
BootstrapNode = StartCodex(s => s.WithName("BOOTSTRAP"));
}
[TearDown]
@@ -1,298 +0,0 @@
using CodexContractsPlugin;
using CodexPlugin;
using GethPlugin;
using KubernetesWorkflow.Types;
using Logging;
using MetricsPlugin;
using NUnit.Framework;
using Utils;
namespace CodexTests.BasicTests
{
[Ignore("Used for debugging continuous tests")]
[TestFixture]
public class ContinuousSubstitute : AutoBootstrapDistTest
{
[Test]
public void ContinuousTestSubstitute()
{
var geth = Ci.StartGethNode(s => s.IsMiner().WithName("geth"));
var contract = Ci.StartCodexContracts(geth);
var group = AddCodex(5, o => o
.EnableMetrics()
.EnableMarketplace(geth, contract, s => s
.WithInitial(10.Eth(), 100000.TestTokens())
.AsStorageNode()
.AsValidator())
.WithBlockTTL(TimeSpan.FromMinutes(5))
.WithBlockMaintenanceInterval(TimeSpan.FromSeconds(10))
.WithBlockMaintenanceNumber(100)
.WithStorageQuota(1.GB()));
var nodes = group.Cast<CodexNode>().ToArray();
var rc = Ci.DeployMetricsCollector(nodes);
var availability = new StorageAvailability(
totalSpace: 500.MB(),
maxDuration: TimeSpan.FromMinutes(5),
minPriceForTotalSpace: 500.TestTokens(),
maxCollateral: 1024.TestTokens()
);
foreach (var node in nodes)
{
node.Marketplace.MakeStorageAvailable(availability);
}
var endTime = DateTime.UtcNow + TimeSpan.FromHours(10);
while (DateTime.UtcNow < endTime)
{
var allNodes = nodes.ToList();
var primary = allNodes.PickOneRandom();
var secondary = allNodes.PickOneRandom();
Log("Run Test");
PerformTest(primary, secondary, rc);
Thread.Sleep(TimeSpan.FromSeconds(5));
}
}
private void LogBytesPerMillisecond(Action action)
{
var sw = Stopwatch.Begin(GetTestLog());
action();
var duration = sw.End();
double totalMs = duration.TotalMilliseconds;
double totalBytes = fileSize.SizeInBytes;
var bytesPerMs = totalBytes / totalMs;
Log($"Bytes per millisecond: {bytesPerMs}");
}
[Test]
public void PeerTest()
{
var group = AddCodex(5, o => o
//.EnableMetrics()
//.EnableMarketplace(100000.TestTokens(), 0.Eth(), isValidator: true)
.WithBlockTTL(TimeSpan.FromMinutes(2))
.WithBlockMaintenanceInterval(TimeSpan.FromMinutes(2))
.WithBlockMaintenanceNumber(10000)
.WithBlockTTL(TimeSpan.FromMinutes(2))
.WithStorageQuota(1.GB()));
var nodes = group.Cast<CodexNode>().ToArray();
var checkTime = DateTime.UtcNow + TimeSpan.FromMinutes(1);
var endTime = DateTime.UtcNow + TimeSpan.FromHours(10);
while (DateTime.UtcNow < endTime)
{
//CreatePeerConnectionTestHelpers().AssertFullyConnected(GetAllOnlineCodexNodes());
//CheckRoutingTables(GetAllOnlineCodexNodes());
var node = nodes.ToList().PickOneRandom();
var file = GenerateTestFile(50.MB());
node.UploadFile(file);
Thread.Sleep(20000);
}
}
private void CheckRoutingTables(IEnumerable<ICodexNode> nodes)
{
var all = nodes.ToArray();
var allIds = all.Select(n => n.GetDebugInfo().Table.LocalNode.NodeId).ToArray();
var errors = all.Select(n => AreAllPresent(n, allIds)).Where(s => !string.IsNullOrEmpty(s)).ToArray();
if (errors.Any())
{
Assert.Fail(string.Join(Environment.NewLine, errors));
}
}
private string AreAllPresent(ICodexNode n, string[] allIds)
{
var info = n.GetDebugInfo();
var known = info.Table.Nodes.Select(n => n.NodeId).ToArray();
var expected = allIds.Where(i => i != info.Table.LocalNode.NodeId).ToArray();
if (!expected.All(ex => known.Contains(ex)))
{
return $"Not all of '{string.Join(",", expected)}' were present in routing table: '{string.Join(",", known)}'";
}
return string.Empty;
}
private ByteSize fileSize = 80.MB();
private const string BytesStoredMetric = "codexRepostoreBytesUsed";
private void PerformTest(ICodexNode primary, ICodexNode secondary, RunningPod rc)
{
ScopedTestFiles(() =>
{
var testFile = GenerateTestFile(fileSize);
var metrics = Ci.WrapMetricsCollector(rc, primary);
var beforeBytesStored = metrics.GetMetric(BytesStoredMetric);
ContentId contentId = null!;
LogBytesPerMillisecond(() => contentId = primary.UploadFile(testFile));
var low = fileSize.SizeInBytes;
var high = low * 1.2;
Log("looking for: " + low + " < " + high);
Time.WaitUntil(() =>
{
var afterBytesStored = metrics.GetMetric(BytesStoredMetric);
var newBytes = Convert.ToInt64(afterBytesStored.Values.Last().Value - beforeBytesStored.Values.Last().Value);
return high > newBytes && newBytes > low;
}, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(2), nameof(ContinuousSubstitute));
FileUtils.TrackedFile? downloadedFile = null;
LogBytesPerMillisecond(() => downloadedFile = secondary.DownloadContent(contentId));
testFile.AssertIsEqual(downloadedFile);
});
}
[Test]
public void HoldMyBeerTest()
{
var blockExpirationTime = TimeSpan.FromMinutes(3);
var group = AddCodex(3, o => o
.EnableMetrics()
.WithBlockTTL(blockExpirationTime)
.WithBlockMaintenanceInterval(TimeSpan.FromMinutes(2))
.WithBlockMaintenanceNumber(10000)
.WithStorageQuota(2000.MB()));
var nodes = group.Cast<CodexNode>().ToArray();
var endTime = DateTime.UtcNow + TimeSpan.FromHours(24);
var filesize = 80.MB();
double codexDefaultBlockSize = 31 * 64 * 33;
var numberOfBlocks = Convert.ToInt64(Math.Ceiling(filesize.SizeInBytes / codexDefaultBlockSize));
var sizeInBytes = filesize.SizeInBytes;
Assert.That(numberOfBlocks, Is.EqualTo(1282));
var startTime = DateTime.UtcNow;
var successfulUploads = 0;
var successfulDownloads = 0;
while (DateTime.UtcNow < endTime)
{
foreach (var node in nodes)
{
try
{
Thread.Sleep(TimeSpan.FromSeconds(5));
ScopedTestFiles(() =>
{
var uploadStartTime = DateTime.UtcNow;
var file = GenerateTestFile(filesize);
var cid = node.UploadFile(file);
var cidTag = cid.Id.Substring(cid.Id.Length - 6);
Measure("upload-log-asserts", () =>
{
var uploadLog = Ci.DownloadLog(node, tailLines: 50000);
var storeLines = uploadLog.FindLinesThatContain("Stored data", "topics=\"codex node\"");
uploadLog.DeleteFile();
var storeLine = GetLineForCidTag(storeLines, cidTag);
AssertStoreLineContains(storeLine, numberOfBlocks, sizeInBytes);
});
successfulUploads++;
var uploadTimeTaken = DateTime.UtcNow - uploadStartTime;
if (uploadTimeTaken >= blockExpirationTime.Subtract(TimeSpan.FromSeconds(10)))
{
Assert.Fail("Upload took too long. Blocks already expired.");
}
var dl = node.DownloadContent(cid);
file.AssertIsEqual(dl);
Measure("download-log-asserts", () =>
{
var downloadLog = Ci.DownloadLog(node, tailLines: 50000);
var sentLines = downloadLog.FindLinesThatContain("Sent bytes", "topics=\"codex restapi\"");
downloadLog.DeleteFile();
var sentLine = GetLineForCidTag(sentLines, cidTag);
AssertSentLineContains(sentLine, sizeInBytes);
});
successfulDownloads++;
});
}
catch
{
var testDuration = DateTime.UtcNow - startTime;
Log("Test failed. Delaying shut-down by 30 seconds to collect metrics.");
Log($"Test failed after {Time.FormatDuration(testDuration)} and {successfulUploads} successful uploads and {successfulDownloads} successful downloads");
Thread.Sleep(TimeSpan.FromSeconds(30));
throw;
}
}
Thread.Sleep(TimeSpan.FromSeconds(5));
}
}
private void AssertSentLineContains(string sentLine, long sizeInBytes)
{
var tag = "bytes=";
var token = sentLine.Substring(sentLine.IndexOf(tag) + tag.Length);
var bytes = Convert.ToInt64(token);
Assert.AreEqual(sizeInBytes, bytes, $"Sent bytes: Number of bytes incorrect. Line: '{sentLine}'");
}
private void AssertStoreLineContains(string storeLine, long numberOfBlocks, long sizeInBytes)
{
var tokens = storeLine.Split(" ");
var blocksToken = GetToken(tokens, "blocks=");
var sizeToken = GetToken(tokens, "size=");
if (blocksToken == null) Assert.Fail("blockToken not found in " + storeLine);
if (sizeToken == null) Assert.Fail("sizeToken not found in " + storeLine);
var blocks = Convert.ToInt64(blocksToken);
var size = Convert.ToInt64(sizeToken?.Replace("'NByte", ""));
var lineLog = $" Line: '{storeLine}'";
Assert.AreEqual(numberOfBlocks, blocks, "Stored data: Number of blocks incorrect." + lineLog);
Assert.AreEqual(sizeInBytes, size, "Stored data: Number of blocks incorrect." + lineLog);
}
private string GetLineForCidTag(string[] lines, string cidTag)
{
var result = lines.SingleOrDefault(l => l.Contains(cidTag));
if (result == null)
{
Assert.Fail($"Failed to find '{cidTag}' in lines: '{string.Join(",", lines)}'");
throw new Exception();
}
return result;
}
private string? GetToken(string[] tokens, string tag)
{
var token = tokens.SingleOrDefault(t => t.StartsWith(tag));
if (token == null) return null;
return token.Substring(tag.Length);
}
}
}
+3 -3
View File
@@ -13,7 +13,7 @@ namespace CodexTests.BasicTests
[Test]
public void CodexLogExample()
{
var primary = AddCodex(s => s.WithLogLevel(CodexLogLevel.Trace, new CodexLogCustomTopics(CodexLogLevel.Warn, CodexLogLevel.Warn)));
var primary = StartCodex(s => s.WithLogLevel(CodexLogLevel.Trace, new CodexLogCustomTopics(CodexLogLevel.Warn, CodexLogLevel.Warn)));
var cid = primary.UploadFile(GenerateTestFile(5.MB()));
@@ -28,8 +28,8 @@ namespace CodexTests.BasicTests
[Test]
public void TwoMetricsExample()
{
var group = AddCodex(2, s => s.EnableMetrics());
var group2 = AddCodex(2, s => s.EnableMetrics());
var group = StartCodex(2, s => s.EnableMetrics());
var group2 = StartCodex(2, s => s.EnableMetrics());
var primary = group[0];
var secondary = group[1];
@@ -21,7 +21,7 @@ namespace CodexTests.BasicTests
var contracts = Ci.StartCodexContracts(geth);
var numberOfHosts = 5;
var hosts = AddCodex(numberOfHosts, s => s
var hosts = StartCodex(numberOfHosts, s => s
.WithName("Host")
.WithLogLevel(CodexLogLevel.Trace, new CodexLogCustomTopics(CodexLogLevel.Error, CodexLogLevel.Error, CodexLogLevel.Warn)
{
@@ -49,7 +49,7 @@ namespace CodexTests.BasicTests
var testFile = GenerateTestFile(fileSize);
var client = AddCodex(s => s
var client = StartCodex(s => s
.WithName("Client")
.EnableMarketplace(geth, contracts, m => m
.WithInitial(10.Eth(), clientInitialBalance)));
@@ -95,7 +95,7 @@ namespace CodexTests.BasicTests
Log($"SlotFilledEvents: {slotFilledEvents.Length} - NumSlots: {purchase.MinRequiredNumberOfNodes}");
if (slotFilledEvents.Length != purchase.MinRequiredNumberOfNodes) throw new Exception();
}, Convert.ToInt32(purchase.Duration.TotalSeconds / 5) + 10, TimeSpan.FromSeconds(5), "Checking SlotFilled events");
}, purchase.Expiry + TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(5), "Checking SlotFilled events");
}
private void AssertStorageRequest(Request request, StoragePurchaseRequest purchase, ICodexContracts contracts, ICodexNode buyer)
@@ -1,5 +1,4 @@
using CodexPlugin;
using DistTestCore;
using NUnit.Framework;
using Utils;
@@ -11,7 +10,7 @@ namespace CodexTests.BasicTests
[Test]
public void OneClientTest()
{
var primary = Ci.StartCodexNode();
var primary = StartCodex();
PerformOneClientTest(primary);
}
@@ -19,11 +18,11 @@ namespace CodexTests.BasicTests
[Test]
public void RestartTest()
{
var primary = Ci.StartCodexNode();
var primary = StartCodex();
primary.Stop(waitTillStopped: true);
primary = Ci.StartCodexNode();
primary = StartCodex();
PerformOneClientTest(primary);
}
@@ -9,8 +9,8 @@ namespace CodexTests.BasicTests
[Test]
public void ThreeClient()
{
var primary = AddCodex();
var secondary = AddCodex();
var primary = StartCodex();
var secondary = StartCodex();
var testFile = GenerateTestFile(10.MB());
@@ -10,8 +10,8 @@ namespace CodexTests.BasicTests
[Test]
public void TwoClientTest()
{
var uploader = AddCodex(s => s.WithName("Uploader"));
var downloader = AddCodex(s => s.WithName("Downloader").WithBootstrapNode(uploader));
var uploader = StartCodex(s => s.WithName("Uploader"));
var downloader = StartCodex(s => s.WithName("Downloader").WithBootstrapNode(uploader));
PerformTwoClientTest(uploader, downloader);
}
+7 -7
View File
@@ -39,22 +39,22 @@ namespace CodexTests
onlineCodexNodes.Remove(lifecycle);
}
public ICodexNode AddCodex()
public ICodexNode StartCodex()
{
return AddCodex(s => { });
return StartCodex(s => { });
}
public ICodexNode AddCodex(Action<ICodexSetup> setup)
public ICodexNode StartCodex(Action<ICodexSetup> setup)
{
return AddCodex(1, setup)[0];
return StartCodex(1, setup)[0];
}
public ICodexNodeGroup AddCodex(int numberOfNodes)
public ICodexNodeGroup StartCodex(int numberOfNodes)
{
return AddCodex(numberOfNodes, s => { });
return StartCodex(numberOfNodes, s => { });
}
public ICodexNodeGroup AddCodex(int numberOfNodes, Action<ICodexSetup> setup)
public ICodexNodeGroup StartCodex(int numberOfNodes, Action<ICodexSetup> setup)
{
var group = Ci.StartCodexNodes(numberOfNodes, s =>
{
@@ -11,7 +11,7 @@ namespace CodexTests.DownloadConnectivityTests
[Test]
public void MetricsDoesNotInterfereWithPeerDownload()
{
AddCodex(2, s => s.EnableMetrics());
StartCodex(2, s => s.EnableMetrics());
AssertAllNodesConnected();
}
@@ -21,7 +21,7 @@ namespace CodexTests.DownloadConnectivityTests
{
var geth = Ci.StartGethNode(s => s.IsMiner());
var contracts = Ci.StartCodexContracts(geth);
AddCodex(2, s => s.EnableMarketplace(geth, contracts, m => m
StartCodex(2, s => s.EnableMarketplace(geth, contracts, m => m
.WithInitial(10.Eth(), 1000.TestTokens())));
AssertAllNodesConnected();
@@ -33,7 +33,7 @@ namespace CodexTests.DownloadConnectivityTests
[Values(2, 5)] int numberOfNodes,
[Values(1, 10)] int sizeMBs)
{
AddCodex(numberOfNodes);
StartCodex(numberOfNodes);
AssertAllNodesConnected(sizeMBs);
}
@@ -8,10 +8,10 @@ namespace CodexTests.PeerDiscoveryTests
[Test]
public void TwoLayersTest()
{
var root = AddCodex();
var l1Source = AddCodex(s => s.WithBootstrapNode(root));
var l1Node = AddCodex(s => s.WithBootstrapNode(root));
var l2Target = AddCodex(s => s.WithBootstrapNode(l1Node));
var root = StartCodex();
var l1Source = StartCodex(s => s.WithBootstrapNode(root));
var l1Node = StartCodex(s => s.WithBootstrapNode(root));
var l2Target = StartCodex(s => s.WithBootstrapNode(l1Node));
AssertAllNodesConnected();
}
@@ -19,11 +19,11 @@ namespace CodexTests.PeerDiscoveryTests
[Test]
public void ThreeLayersTest()
{
var root = AddCodex();
var l1Source = AddCodex(s => s.WithBootstrapNode(root));
var l1Node = AddCodex(s => s.WithBootstrapNode(root));
var l2Node = AddCodex(s => s.WithBootstrapNode(l1Node));
var l3Target = AddCodex(s => s.WithBootstrapNode(l2Node));
var root = StartCodex();
var l1Source = StartCodex(s => s.WithBootstrapNode(root));
var l1Node = StartCodex(s => s.WithBootstrapNode(root));
var l2Node = StartCodex(s => s.WithBootstrapNode(l1Node));
var l3Target = StartCodex(s => s.WithBootstrapNode(l2Node));
AssertAllNodesConnected();
}
@@ -33,10 +33,10 @@ namespace CodexTests.PeerDiscoveryTests
[TestCase(10)]
public void NodeChainTest(int chainLength)
{
var node = AddCodex();
var node = StartCodex();
for (var i = 1; i < chainLength; i++)
{
node = AddCodex(s => s.WithBootstrapNode(node));
node = StartCodex(s => s.WithBootstrapNode(node));
}
AssertAllNodesConnected();
@@ -12,7 +12,7 @@ namespace CodexTests.PeerDiscoveryTests
public void CanReportUnknownPeerId()
{
var unknownId = "16Uiu2HAkv2CHWpff3dj5iuVNERAp8AGKGNgpGjPexJZHSqUstfsK";
var node = AddCodex();
var node = StartCodex();
var result = node.GetDebugPeer(unknownId);
Assert.That(result.IsPeerFound, Is.False);
@@ -21,7 +21,7 @@ namespace CodexTests.PeerDiscoveryTests
[Test]
public void MetricsDoesNotInterfereWithPeerDiscovery()
{
AddCodex(2, s => s.EnableMetrics());
StartCodex(2, s => s.EnableMetrics());
AssertAllNodesConnected();
}
@@ -31,7 +31,7 @@ namespace CodexTests.PeerDiscoveryTests
{
var geth = Ci.StartGethNode(s => s.IsMiner());
var contracts = Ci.StartCodexContracts(geth);
AddCodex(2, s => s.EnableMarketplace(geth, contracts, m => m
StartCodex(2, s => s.EnableMarketplace(geth, contracts, m => m
.WithInitial(10.Eth(), 1000.TestTokens())));
AssertAllNodesConnected();
@@ -42,7 +42,7 @@ namespace CodexTests.PeerDiscoveryTests
[TestCase(10)]
public void VariableNodes(int number)
{
AddCodex(number);
StartCodex(number);
AssertAllNodesConnected();
}
@@ -1,39 +0,0 @@
using CodexPlugin;
using DistTestCore;
using NUnit.Framework;
using Utils;
namespace CodexTests.ScalabilityTests
{
[TestFixture]
public class OneClientLargeFileTests : CodexDistTest
{
[Test]
[Combinatorial]
[UseLongTimeouts]
public void OneClientLargeFile([Values(
256,
512,
1024, // GB
2048,
4096,
8192,
16384,
32768,
65536,
131072
)] int sizeMb)
{
var testFile = GenerateTestFile(sizeMb.MB());
var node = AddCodex(s => s
.WithLogLevel(CodexLogLevel.Warn)
.WithStorageQuota((sizeMb + 10).MB())
);
var contentId = node.UploadFile(testFile);
var downloadedFile = node.DownloadContent(contentId);
testFile.AssertIsEqual(downloadedFile);
}
}
}
@@ -12,6 +12,7 @@ namespace CodexTests.ScalabilityTests
[Test]
[Combinatorial]
[Ignore("Used to measure disc io speeds in cluster.")]
public void DiscSpeedTest(
[Values(1, 10, 100, 1024, 1024 * 10, 1024 * 100, 1024 * 1024)] int bufferSizeKb
)
@@ -5,12 +5,13 @@ using GethPlugin;
using NUnit.Framework;
using Utils;
namespace CodexTests.BasicTests
namespace CodexTests.UtilityTests
{
[TestFixture]
public class DiscordBotTests : AutoBootstrapDistTest
{
[Test]
[Ignore("Used for debugging bots")]
public void BotRewardTest()
{
var myAccount = EthAccount.GenerateNew();
@@ -57,7 +58,7 @@ namespace CodexTests.BasicTests
for (var i = 0; i < numberOfHosts; i++)
{
var seller = AddCodex(s => s
var seller = StartCodex(s => s
.WithName("Seller")
.WithLogLevel(CodexLogLevel.Trace, new CodexLogCustomTopics(CodexLogLevel.Error, CodexLogLevel.Error, CodexLogLevel.Warn)
{
@@ -81,7 +82,7 @@ namespace CodexTests.BasicTests
var testFile = GenerateTestFile(fileSize);
var buyer = AddCodex(s => s
var buyer = StartCodex(s => s
.WithName("Buyer")
.EnableMarketplace(geth, contracts, m => m
.WithAccount(myAccount)
@@ -0,0 +1,56 @@
using CodexPlugin;
using NUnit.Framework;
using Utils;
namespace CodexTests.UtilityTests
{
[TestFixture]
public class LogHelperTests : AutoBootstrapDistTest
{
[Test]
[Ignore("Used to find the most common log messages.")]
public void FindMostCommonLogMessages()
{
var uploader = StartCodex(s => s.WithName("uploader").WithLogLevel(CodexLogLevel.Trace));
var downloader = StartCodex(s => s.WithName("downloader").WithLogLevel(CodexLogLevel.Trace));
var cid = uploader.UploadFile(GenerateTestFile(100.MB()));
Thread.Sleep(1000);
var logStartUtc = DateTime.UtcNow;
Thread.Sleep(1000);
downloader.DownloadContent(cid);
var map = GetLogMap(downloader, logStartUtc).OrderByDescending(p => p.Value);
Log("Downloader - Receive");
foreach (var entry in map)
{
if (entry.Value > 9)
{
Log($"'{entry.Key}' = {entry.Value}");
}
}
}
private Dictionary<string, int> GetLogMap(ICodexNode node, DateTime? startUtc = null)
{
var log = Ci.DownloadLog(node);
var map = new Dictionary<string, int>();
log.IterateLines(line =>
{
var log = CodexLogLine.Parse(line);
if (log == null) return;
if (startUtc.HasValue)
{
if (log.TimestampUtc < startUtc) return;
}
if (map.ContainsKey(log.Message)) map[log.Message] += 1;
else map.Add(log.Message, 1);
});
return map;
}
}
}
@@ -3,7 +3,7 @@ using DistTestCore;
using NUnit.Framework;
using Utils;
namespace CodexTests.BasicTests
namespace CodexTests.UtilityTests
{
// Warning!
// This is a test to check network-isolation in the test-infrastructure.
+2 -2
View File
@@ -24,7 +24,7 @@ namespace BiblioTech
public bool IsAdminChannel(IChannel channel)
{
return channel.Name == Program.Config.AdminChannelName;
return channel.Id == Program.Config.AdminChannelId;
}
public ISocketMessageChannel GetAdminChannel()
@@ -45,7 +45,7 @@ namespace BiblioTech
private void UpdateAdminIds()
{
lastUpdate = DateTime.UtcNow;
var adminRole = guild.Roles.Single(r => r.Name == Program.Config.AdminRoleName);
var adminRole = guild.Roles.Single(r => r.Id == Program.Config.AdminRoleId);
adminIds = adminRole.Members.Select(m => m.Id).ToArray();
}
}
+1 -1
View File
@@ -22,7 +22,7 @@ namespace BiblioTech
private async Task Client_Ready()
{
var guild = client.Guilds.Single(g => g.Name == Program.Config.ServerName);
var guild = client.Guilds.Single(g => g.Id == Program.Config.ServerId);
Program.AdminChecker.SetGuild(guild);
Program.Log.Log($"Initializing for guild: '{guild.Name}'");
+14 -14
View File
@@ -7,31 +7,31 @@ namespace BiblioTech
[Uniform("token", "t", "TOKEN", true, "Discord Application Token")]
public string ApplicationToken { get; set; } = string.Empty;
[Uniform("server-name", "sn", "SERVERNAME", true, "Name of the Discord server")]
public string ServerName { get; set; } = string.Empty;
[Uniform("server-id", "sn", "SERVERID", true, "ID of the Discord server")]
public ulong ServerId { get; set; }
[Uniform("datapath", "dp", "DATAPATH", false, "Root path where all data files will be saved.")]
[Uniform("datapath", "dp", "DATAPATH", true, "Root path where all data files will be saved.")]
public string DataPath { get; set; } = "datapath";
[Uniform("admin-role", "a", "ADMINROLE", true, "Name of the Discord server admin role")]
public string AdminRoleName { get; set; } = string.Empty;
[Uniform("admin-role-id", "a", "ADMINROLEID", true, "ID of the Discord server admin role")]
public ulong AdminRoleId { get; set; }
[Uniform("admin-channel-name", "ac", "ADMINCHANNELNAME", true, "Name of the Discord server channel where admin commands are allowed.")]
public string AdminChannelName { get; set; } = "admin-channel";
[Uniform("admin-channel-id", "ac", "ADMINCHANNELID", true, "ID of the Discord server channel where admin commands are allowed.")]
public ulong AdminChannelId{ get; set; }
[Uniform("rewards-channel-name", "rc", "REWARDSCHANNELNAME", false, "Name of the Discord server channel where participation rewards will be announced.")]
public string RewardsChannelName { get; set; } = "";
[Uniform("rewards-channel-id", "rc", "REWARDSCHANNELID", false, "ID of the Discord server channel where participation rewards will be announced.")]
public ulong RewardsChannelId { get; set; }
[Uniform("chain-events-channel-name", "cc", "CHAINEVENTSCHANNELNAME", false, "Name of the Discord server channel where chain events will be posted.")]
public string ChainEventsChannelName { get; set; } = "";
[Uniform("chain-events-channel-id", "cc", "CHAINEVENTSCHANNELID", false, "ID of the Discord server channel where chain events will be posted.")]
public ulong ChainEventsChannelId { get; set; }
[Uniform("reward-api-port", "rp", "REWARDAPIPORT", false, "TCP listen port for the reward API.")]
[Uniform("reward-api-port", "rp", "REWARDAPIPORT", true, "TCP listen port for the reward API.")]
public int RewardApiPort { get; set; } = 31080;
[Uniform("send-eth", "se", "SENDETH", false, "Amount of Eth send by the mint command. Default: 10.")]
[Uniform("send-eth", "se", "SENDETH", true, "Amount of Eth send by the mint command.")]
public int SendEth { get; set; } = 10;
[Uniform("mint-tt", "mt", "MINTTT", false, "Amount of TestTokens minted by the mint command. Default: 1073741824")]
[Uniform("mint-tt", "mt", "MINTTT", true, "Amount of TestTokens minted by the mint command.")]
public int MintTT { get; set; } = 1073741824;
public string EndpointsPath
+2
View File
@@ -21,6 +21,8 @@ namespace BiblioTech
public static Task Main(string[] args)
{
Log = new ConsoleLog();
var uniformArgs = new ArgsUniform<Configuration>(PrintHelp, args);
Config = uniformArgs.Parse();
+8 -8
View File
@@ -16,8 +16,8 @@ namespace BiblioTech.Rewards
{
this.client = client;
rewardsChannel = GetChannel(Program.Config.RewardsChannelName);
eventsChannel = GetChannel(Program.Config.ChainEventsChannelName);
rewardsChannel = GetChannel(Program.Config.RewardsChannelId);
eventsChannel = GetChannel(Program.Config.ChainEventsChannelId);
}
public async Task GiveRewards(GiveRewardsCommand rewards)
@@ -45,10 +45,10 @@ namespace BiblioTech.Rewards
await context.ProcessGiveRewardsCommand(LookUpUsers(rewards));
}
private SocketTextChannel? GetChannel(string name)
private SocketTextChannel? GetChannel(ulong id)
{
if (string.IsNullOrEmpty(name)) return null;
return GetGuild().TextChannels.SingleOrDefault(c => c.Name == name);
if (id == 0) return null;
return GetGuild().TextChannels.SingleOrDefault(c => c.Id == id);
}
private async Task ProcessChainEvents(string[] eventsOverview)
@@ -147,11 +147,11 @@ namespace BiblioTech.Rewards
private SocketGuild GetGuild()
{
var guild = client.Guilds.SingleOrDefault(g => g.Name == Program.Config.ServerName);
var guild = client.Guilds.SingleOrDefault(g => g.Id == Program.Config.ServerId);
if (guild == null)
{
throw new Exception($"Unable to find guild by name: '{Program.Config.ServerName}'. " +
$"Known guilds: [{string.Join(",", client.Guilds.Select(g => g.Name))}]");
throw new Exception($"Unable to find guild by id: '{Program.Config.ServerId}'. " +
$"Known guilds: [{string.Join(",", client.Guilds.Select(g => g.Name + " (" + g.Id + ")"))}]");
}
return guild;
}
+2 -2
View File
@@ -255,9 +255,9 @@ namespace CodexNetDeployer
return TimeSpan.FromSeconds(2);
}
public int HttpMaxNumberOfRetries()
public TimeSpan HttpRetryTimeout()
{
return 2;
return TimeSpan.FromSeconds(30);
}
public TimeSpan HttpCallTimeout()
+4 -4
View File
@@ -4,16 +4,16 @@ namespace TestNetRewarder
{
public class Configuration
{
[Uniform("datapath", "dp", "DATAPATH", false, "Root path where all data files will be saved.")]
[Uniform("datapath", "dp", "DATAPATH", true, "Root path where all data files will be saved.")]
public string DataPath { get; set; } = "datapath";
[Uniform("discordbot-host", "dh", "DISCORDBOTHOST", true, "http address of the discord bot.")]
public string DiscordHost { get; set; } = "host";
[Uniform("discordbot-port", "dp", "DISCORDBOTPORT", true, "port number of the discord bot reward API. (31080 by default)")]
[Uniform("discordbot-port", "dp", "DISCORDBOTPORT", true, "port number of the discord bot reward API.")]
public int DiscordPort { get; set; } = 31080;
[Uniform("interval-minutes", "im", "INTERVALMINUTES", false, "time in minutes between reward updates. (default 15)")]
[Uniform("interval-minutes", "im", "INTERVALMINUTES", true, "time in minutes between reward updates.")]
public int IntervalMinutes { get; set; } = 15;
[Uniform("check-history", "ch", "CHECKHISTORY", true, "Unix epoc timestamp of a moment in history on which processing begins. Required for hosting rewards. Should be 'launch of the testnet'.")]
@@ -22,7 +22,7 @@ namespace TestNetRewarder
[Uniform("market-insights", "mi", "MARKETINSIGHTS", false, "Semi-colon separated integers. Each represents a multiple of intervals, for which a market insights average will be generated.")]
public string MarketInsights { get; set; } = "1;96";
[Uniform("events-overview", "eo", "EVENTSOVERVIEW", false, "When greater than zero, chain event summary will be generated. (default 1)")]
[Uniform("events-overview", "eo", "EVENTSOVERVIEW", false, "When greater than zero, chain event summary will be generated.")]
public int CreateChainEventsOverview { get; set; } = 1;
public string LogPath