Merge branch 'master' into feature/continuous-testing
This commit is contained in:
commit
91a8f6a869
|
@ -8,16 +8,16 @@ namespace CodexNetDeployer
|
||||||
public class Configuration
|
public class Configuration
|
||||||
{
|
{
|
||||||
[Uniform("codex-image", "ci", "CODEXIMAGE", true, "Docker image of Codex.")]
|
[Uniform("codex-image", "ci", "CODEXIMAGE", true, "Docker image of Codex.")]
|
||||||
public string CodexImage { get; set; } = string.Empty;
|
public string CodexImage { get; set; } = CodexContainerRecipe.DockerImage;
|
||||||
|
|
||||||
[Uniform("geth-image", "gi", "GETHIMAGE", true, "Docker image of Geth.")]
|
[Uniform("geth-image", "gi", "GETHIMAGE", true, "Docker image of Geth.")]
|
||||||
public string GethImage { get; set; } = string.Empty;
|
public string GethImage { get; set; } = GethContainerRecipe.DockerImage;
|
||||||
|
|
||||||
[Uniform("contracts-image", "oi", "CONTRACTSIMAGE", true, "Docker image of Codex Contracts.")]
|
[Uniform("contracts-image", "oi", "CONTRACTSIMAGE", true, "Docker image of Codex Contracts.")]
|
||||||
public string ContractsImage { get; set; } = string.Empty;
|
public string ContractsImage { get; set; } = CodexContractsContainerRecipe.DockerImage;
|
||||||
|
|
||||||
[Uniform("kube-config", "kc", "KUBECONFIG", true, "Path to Kubeconfig file.")]
|
[Uniform("kube-config", "kc", "KUBECONFIG", false, "Path to Kubeconfig file. Use 'null' (default) to use local cluster.")]
|
||||||
public string KubeConfigFile { get; set; } = string.Empty;
|
public string KubeConfigFile { get; set; } = "null";
|
||||||
|
|
||||||
[Uniform("kube-namespace", "kn", "KUBENAMESPACE", true, "Kubernetes namespace to be used for deployment.")]
|
[Uniform("kube-namespace", "kn", "KUBENAMESPACE", true, "Kubernetes namespace to be used for deployment.")]
|
||||||
public string KubeNamespace { get; set; } = string.Empty;
|
public string KubeNamespace { get; set; } = string.Empty;
|
||||||
|
@ -32,18 +32,10 @@ namespace CodexNetDeployer
|
||||||
public int? StorageQuota { get; set; }
|
public int? StorageQuota { get; set; }
|
||||||
|
|
||||||
[Uniform("log-level", "l", "LOGLEVEL", true, "Log level used by each Codex node. [Trace, Debug*, Info, Warn, Error]")]
|
[Uniform("log-level", "l", "LOGLEVEL", true, "Log level used by each Codex node. [Trace, Debug*, Info, Warn, Error]")]
|
||||||
public CodexLogLevel CodexLogLevel { get; set; }
|
public CodexLogLevel CodexLogLevel { get; set; } = CodexLogLevel.Debug;
|
||||||
|
|
||||||
public TestRunnerLocation RunnerLocation { get; set; } = TestRunnerLocation.InternalToCluster;
|
public TestRunnerLocation RunnerLocation { get; set; } = TestRunnerLocation.InternalToCluster;
|
||||||
|
|
||||||
public class Defaults
|
|
||||||
{
|
|
||||||
public string CodexImage { get; set; } = CodexContainerRecipe.DockerImage;
|
|
||||||
public string GethImage { get; set; } = GethContainerRecipe.DockerImage;
|
|
||||||
public string ContractsImage { get; set; } = CodexContractsContainerRecipe.DockerImage;
|
|
||||||
public CodexLogLevel CodexLogLevel { get; set; } = CodexLogLevel.Debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<string> Validate()
|
public List<string> Validate()
|
||||||
{
|
{
|
||||||
var errors = new List<string>();
|
var errors = new List<string>();
|
||||||
|
@ -74,7 +66,7 @@ namespace CodexNetDeployer
|
||||||
{
|
{
|
||||||
if (value == null || value.Value < 1)
|
if (value == null || value.Value < 1)
|
||||||
{
|
{
|
||||||
errors.Add($"{variable} is must be set and must be greater than 0.");
|
errors.Add($"{variable} must be set and must be greater than 0.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +74,7 @@ namespace CodexNetDeployer
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(value))
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
{
|
{
|
||||||
errors.Add($"{variable} is must be set.");
|
errors.Add($"{variable} must be set.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,9 +51,11 @@ namespace CodexNetDeployer
|
||||||
|
|
||||||
private (WorkflowCreator, TestLifecycle) CreateFacilities()
|
private (WorkflowCreator, TestLifecycle) CreateFacilities()
|
||||||
{
|
{
|
||||||
|
var kubeConfig = GetKubeConfig(config.KubeConfigFile);
|
||||||
|
|
||||||
var lifecycleConfig = new DistTestCore.Configuration
|
var lifecycleConfig = new DistTestCore.Configuration
|
||||||
(
|
(
|
||||||
kubeConfigFile: config.KubeConfigFile,
|
kubeConfigFile: kubeConfig,
|
||||||
logPath: "null",
|
logPath: "null",
|
||||||
logDebug: false,
|
logDebug: false,
|
||||||
dataFilesPath: "notUsed",
|
dataFilesPath: "notUsed",
|
||||||
|
@ -61,18 +63,24 @@ namespace CodexNetDeployer
|
||||||
runnerLocation: config.RunnerLocation
|
runnerLocation: config.RunnerLocation
|
||||||
);
|
);
|
||||||
|
|
||||||
var kubeConfig = new KubernetesWorkflow.Configuration(
|
var kubeFlowConfig = new KubernetesWorkflow.Configuration(
|
||||||
k8sNamespacePrefix: config.KubeNamespace,
|
k8sNamespacePrefix: config.KubeNamespace,
|
||||||
kubeConfigFile: config.KubeConfigFile,
|
kubeConfigFile: kubeConfig,
|
||||||
operationTimeout: timeset.K8sOperationTimeout(),
|
operationTimeout: timeset.K8sOperationTimeout(),
|
||||||
retryDelay: timeset.WaitForK8sServiceDelay());
|
retryDelay: timeset.WaitForK8sServiceDelay());
|
||||||
|
|
||||||
var workflowCreator = new WorkflowCreator(log, kubeConfig, testNamespacePostfix: string.Empty);
|
var workflowCreator = new WorkflowCreator(log, kubeFlowConfig, testNamespacePostfix: string.Empty);
|
||||||
var lifecycle = new TestLifecycle(log, lifecycleConfig, timeset, workflowCreator);
|
var lifecycle = new TestLifecycle(log, lifecycleConfig, timeset, workflowCreator);
|
||||||
|
|
||||||
return (workflowCreator, lifecycle);
|
return (workflowCreator, lifecycle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string? GetKubeConfig(string kubeConfigFile)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(kubeConfigFile) || kubeConfigFile.ToLowerInvariant() == "null") return null;
|
||||||
|
return kubeConfigFile;
|
||||||
|
}
|
||||||
|
|
||||||
private DeploymentMetadata CreateMetadata()
|
private DeploymentMetadata CreateMetadata()
|
||||||
{
|
{
|
||||||
return new DeploymentMetadata(
|
return new DeploymentMetadata(
|
||||||
|
|
|
@ -17,7 +17,7 @@ public class Program
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var uniformArgs = new ArgsUniform<Configuration>(new Configuration.Defaults(), args);
|
var uniformArgs = new ArgsUniform<Configuration>(args);
|
||||||
var config = uniformArgs.Parse(true);
|
var config = uniformArgs.Parse(true);
|
||||||
|
|
||||||
if (args.Any(a => a == "--external"))
|
if (args.Any(a => a == "--external"))
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -18,8 +18,8 @@ namespace ContinuousTests
|
||||||
[Uniform("keep", "k", "KEEP", false, "Set to '1' to retain logs of successful tests.")]
|
[Uniform("keep", "k", "KEEP", false, "Set to '1' to retain logs of successful tests.")]
|
||||||
public bool KeepPassedTestLogs { get; set; } = false;
|
public bool KeepPassedTestLogs { get; set; } = false;
|
||||||
|
|
||||||
[Uniform("kube-config", "kc", "KUBECONFIG", true, "Path to Kubeconfig file.")]
|
[Uniform("kube-config", "kc", "KUBECONFIG", true, "Path to Kubeconfig file. Use 'null' (default) to use local cluster.")]
|
||||||
public string KubeConfigFile { get; set; } = string.Empty;
|
public string KubeConfigFile { get; set; } = "null";
|
||||||
|
|
||||||
public CodexDeployment CodexDeployment { get; set; } = null!;
|
public CodexDeployment CodexDeployment { get; set; } = null!;
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ namespace ContinuousTests
|
||||||
{
|
{
|
||||||
var uniformArgs = new ArgsUniform<Configuration>(args);
|
var uniformArgs = new ArgsUniform<Configuration>(args);
|
||||||
|
|
||||||
var result = uniformArgs.Parse();
|
var result = uniformArgs.Parse(true);
|
||||||
|
|
||||||
result.CodexDeployment = ParseCodexDeploymentJson(result.CodexDeploymentJson);
|
result.CodexDeployment = ParseCodexDeploymentJson(result.CodexDeploymentJson);
|
||||||
|
|
||||||
|
|
|
@ -20,15 +20,18 @@ namespace ContinuousTests
|
||||||
startupChecker.Check();
|
startupChecker.Check();
|
||||||
|
|
||||||
var overviewLog = new FixtureLog(new LogConfig(config.LogPath, false), "Overview");
|
var overviewLog = new FixtureLog(new LogConfig(config.LogPath, false), "Overview");
|
||||||
|
overviewLog.Log("Continuous tests starting...");
|
||||||
var allTests = testFactory.CreateTests();
|
var allTests = testFactory.CreateTests();
|
||||||
var testStarters = allTests.Select(t => new TestStarter(config, overviewLog, t.GetType(), t.RunTestEvery)).ToArray();
|
var testLoop = allTests.Select(t => new TestLoop(config, overviewLog, t.GetType(), t.RunTestEvery)).ToArray();
|
||||||
|
|
||||||
foreach (var t in testStarters)
|
foreach (var t in testLoop)
|
||||||
{
|
{
|
||||||
|
overviewLog.Log("Launching test-loop for " + t.Name);
|
||||||
t.Begin();
|
t.Begin();
|
||||||
Thread.Sleep(TimeSpan.FromMinutes(5));
|
Thread.Sleep(TimeSpan.FromMinutes(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overviewLog.Log("All test-loops launched.");
|
||||||
while (true) Thread.Sleep((2 ^ 31) - 1);
|
while (true) Thread.Sleep((2 ^ 31) - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\ArgsUniform\ArgsUniform.csproj" />
|
<ProjectReference Include="..\ArgsUniform\ArgsUniform.csproj" />
|
||||||
<ProjectReference Include="..\DistTestCore\DistTestCore.csproj" />
|
<ProjectReference Include="..\DistTestCore\DistTestCore.csproj" />
|
||||||
|
<ProjectReference Include="..\KubernetesWorkflow\KubernetesWorkflow.csproj" />
|
||||||
<ProjectReference Include="..\Logging\Logging.csproj" />
|
<ProjectReference Include="..\Logging\Logging.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ using Logging;
|
||||||
using Utils;
|
using Utils;
|
||||||
using KubernetesWorkflow;
|
using KubernetesWorkflow;
|
||||||
using NUnit.Framework.Internal;
|
using NUnit.Framework.Internal;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace ContinuousTests
|
namespace ContinuousTests
|
||||||
{
|
{
|
||||||
|
@ -41,6 +42,22 @@ namespace ContinuousTests
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
RunTest();
|
RunTest();
|
||||||
|
fileManager.DeleteAllTestFiles();
|
||||||
|
Directory.Delete(dataFolder, true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
overviewLog.Error("Test infra failure: SingleTestRun failed with " + ex);
|
||||||
|
Environment.Exit(-1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunTest()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RunTestMoments();
|
||||||
|
|
||||||
if (!config.KeepPassedTestLogs) fixtureLog.Delete();
|
if (!config.KeepPassedTestLogs) fixtureLog.Delete();
|
||||||
}
|
}
|
||||||
|
@ -49,12 +66,9 @@ namespace ContinuousTests
|
||||||
fixtureLog.Error("Test run failed with exception: " + ex);
|
fixtureLog.Error("Test run failed with exception: " + ex);
|
||||||
fixtureLog.MarkAsFailed();
|
fixtureLog.MarkAsFailed();
|
||||||
}
|
}
|
||||||
fileManager.DeleteAllTestFiles();
|
|
||||||
Directory.Delete(dataFolder, true);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RunTest()
|
private void RunTestMoments()
|
||||||
{
|
{
|
||||||
var earliestMoment = handle.GetEarliestMoment();
|
var earliestMoment = handle.GetEarliestMoment();
|
||||||
|
|
||||||
|
@ -66,7 +80,7 @@ namespace ContinuousTests
|
||||||
if (handle.Test.TestFailMode == TestFailMode.StopAfterFirstFailure && exceptions.Any())
|
if (handle.Test.TestFailMode == TestFailMode.StopAfterFirstFailure && exceptions.Any())
|
||||||
{
|
{
|
||||||
Log("Exception detected. TestFailMode = StopAfterFirstFailure. Stopping...");
|
Log("Exception detected. TestFailMode = StopAfterFirstFailure. Stopping...");
|
||||||
throw exceptions.Single();
|
ThrowFailTest();
|
||||||
}
|
}
|
||||||
|
|
||||||
var nextMoment = handle.GetNextMoment(t);
|
var nextMoment = handle.GetNextMoment(t);
|
||||||
|
@ -80,9 +94,7 @@ namespace ContinuousTests
|
||||||
{
|
{
|
||||||
if (exceptions.Any())
|
if (exceptions.Any())
|
||||||
{
|
{
|
||||||
var ex = exceptions.First();
|
ThrowFailTest();
|
||||||
OverviewLog(" > Test failed: " + ex);
|
|
||||||
throw ex;
|
|
||||||
}
|
}
|
||||||
OverviewLog(" > Test passed.");
|
OverviewLog(" > Test passed.");
|
||||||
return;
|
return;
|
||||||
|
@ -90,6 +102,28 @@ namespace ContinuousTests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ThrowFailTest()
|
||||||
|
{
|
||||||
|
var ex = UnpackException(exceptions.First());
|
||||||
|
Log(ex.ToString());
|
||||||
|
OverviewLog(" > Test failed: " + ex.Message);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Exception UnpackException(Exception exception)
|
||||||
|
{
|
||||||
|
if (exception is AggregateException a)
|
||||||
|
{
|
||||||
|
return UnpackException(a.InnerExceptions.First());
|
||||||
|
}
|
||||||
|
if (exception is TargetInvocationException t)
|
||||||
|
{
|
||||||
|
return UnpackException(t.InnerException!);
|
||||||
|
}
|
||||||
|
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
|
||||||
private void RunMoment(int t)
|
private void RunMoment(int t)
|
||||||
{
|
{
|
||||||
using (var context = new TestExecutionContext.IsolatedContext())
|
using (var context = new TestExecutionContext.IsolatedContext())
|
||||||
|
@ -100,7 +134,6 @@ namespace ContinuousTests
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log($" > TestMoment yielded exception: " + ex);
|
|
||||||
exceptions.Add(ex);
|
exceptions.Add(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,30 +2,42 @@
|
||||||
|
|
||||||
namespace ContinuousTests
|
namespace ContinuousTests
|
||||||
{
|
{
|
||||||
public class TestStarter
|
public class TestLoop
|
||||||
{
|
{
|
||||||
private readonly Configuration config;
|
private readonly Configuration config;
|
||||||
private readonly BaseLog overviewLog;
|
private readonly BaseLog overviewLog;
|
||||||
private readonly Type testType;
|
private readonly Type testType;
|
||||||
private readonly TimeSpan runsEvery;
|
private readonly TimeSpan runsEvery;
|
||||||
|
|
||||||
public TestStarter(Configuration config, BaseLog overviewLog, Type testType, TimeSpan runsEvery)
|
public TestLoop(Configuration config, BaseLog overviewLog, Type testType, TimeSpan runsEvery)
|
||||||
{
|
{
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.overviewLog = overviewLog;
|
this.overviewLog = overviewLog;
|
||||||
this.testType = testType;
|
this.testType = testType;
|
||||||
this.runsEvery = runsEvery;
|
this.runsEvery = runsEvery;
|
||||||
|
|
||||||
|
Name = testType.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
public void Begin()
|
public void Begin()
|
||||||
{
|
{
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
StartTest();
|
StartTest();
|
||||||
Thread.Sleep(runsEvery);
|
Thread.Sleep(runsEvery);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
overviewLog.Error("Test infra failure: TestLoop failed with " + ex);
|
||||||
|
Environment.Exit(-1);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ using DistTestCore.Codex;
|
||||||
using DistTestCore.Marketplace;
|
using DistTestCore.Marketplace;
|
||||||
using KubernetesWorkflow;
|
using KubernetesWorkflow;
|
||||||
using Logging;
|
using Logging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace ContinuousTests.Tests
|
namespace ContinuousTests.Tests
|
||||||
|
@ -11,18 +12,17 @@ namespace ContinuousTests.Tests
|
||||||
{
|
{
|
||||||
public override int RequiredNumberOfNodes => 1;
|
public override int RequiredNumberOfNodes => 1;
|
||||||
public override TimeSpan RunTestEvery => TimeSpan.FromDays(4);
|
public override TimeSpan RunTestEvery => TimeSpan.FromDays(4);
|
||||||
public override TestFailMode TestFailMode => TestFailMode.AlwaysRunAllMoments;
|
public override TestFailMode TestFailMode => TestFailMode.StopAfterFirstFailure;
|
||||||
|
|
||||||
public const int EthereumAccountIndex = 200; // TODO: Check against all other account indices of all other tests.
|
public const int EthereumAccountIndex = 200; // TODO: Check against all other account indices of all other tests.
|
||||||
|
public const string MarketplaceTestNamespace = "codex-continuous-marketplace"; // prevent clashes too
|
||||||
|
|
||||||
private const string MarketplaceTestNamespace = "codex-continuous-marketplace";
|
private readonly uint numberOfSlots = 3;
|
||||||
|
private readonly ByteSize fileSize = 10.MB();
|
||||||
private readonly ByteSize fileSize = 100.MB();
|
private readonly TestToken pricePerSlotPerSecond = 10.TestTokens();
|
||||||
private readonly TestToken pricePerBytePerSecond = 1.TestTokens();
|
|
||||||
|
|
||||||
private TestFile file = null!;
|
private TestFile file = null!;
|
||||||
private ContentId? cid;
|
private ContentId? cid;
|
||||||
private TestToken startingBalance = null!;
|
|
||||||
private string purchaseId = string.Empty;
|
private string purchaseId = string.Empty;
|
||||||
|
|
||||||
[TestMoment(t: Zero)]
|
[TestMoment(t: Zero)]
|
||||||
|
@ -30,22 +30,28 @@ namespace ContinuousTests.Tests
|
||||||
{
|
{
|
||||||
var contractDuration = TimeSpan.FromDays(3) + TimeSpan.FromHours(1);
|
var contractDuration = TimeSpan.FromDays(3) + TimeSpan.FromHours(1);
|
||||||
decimal totalDurationSeconds = Convert.ToDecimal(contractDuration.TotalSeconds);
|
decimal totalDurationSeconds = Convert.ToDecimal(contractDuration.TotalSeconds);
|
||||||
var expectedTotalCost = pricePerBytePerSecond.Amount * totalDurationSeconds;
|
var expectedTotalCost = numberOfSlots * pricePerSlotPerSecond.Amount * (totalDurationSeconds + 1);
|
||||||
|
Log.Log("expected total cost: " + expectedTotalCost);
|
||||||
|
|
||||||
file = FileManager.GenerateTestFile(fileSize);
|
file = FileManager.GenerateTestFile(fileSize);
|
||||||
|
|
||||||
var (workflowCreator, lifecycle) = CreateFacilities();
|
var (workflowCreator, lifecycle) = CreateFacilities();
|
||||||
var flow = workflowCreator.CreateWorkflow();
|
var flow = workflowCreator.CreateWorkflow();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var debugInfo = Nodes[0].GetDebugInfo();
|
||||||
|
Assert.That(!string.IsNullOrEmpty(debugInfo.spr));
|
||||||
|
|
||||||
var startupConfig = new StartupConfig();
|
var startupConfig = new StartupConfig();
|
||||||
var codexStartConfig = new CodexStartupConfig(CodexLogLevel.Debug);
|
var codexStartConfig = new CodexStartupConfig(CodexLogLevel.Debug);
|
||||||
codexStartConfig.MarketplaceConfig = new MarketplaceInitialConfig(0.Eth(), 0.TestTokens(), false);
|
codexStartConfig.MarketplaceConfig = new MarketplaceInitialConfig(0.Eth(), 0.TestTokens(), false);
|
||||||
codexStartConfig.MarketplaceConfig.AccountIndexOverride = EthereumAccountIndex;
|
codexStartConfig.MarketplaceConfig.AccountIndexOverride = EthereumAccountIndex;
|
||||||
|
codexStartConfig.BootstrapSpr = debugInfo.spr;
|
||||||
startupConfig.Add(codexStartConfig);
|
startupConfig.Add(codexStartConfig);
|
||||||
startupConfig.Add(Configuration.CodexDeployment.GethStartResult);
|
startupConfig.Add(Configuration.CodexDeployment.GethStartResult);
|
||||||
var rc = flow.Start(1, Location.Unspecified, new CodexContainerRecipe(), startupConfig);
|
var rc = flow.Start(1, Location.Unspecified, new CodexContainerRecipe(), startupConfig);
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var account = Configuration.CodexDeployment.GethStartResult.MarketplaceNetwork.Bootstrap.AllAccounts.Accounts[EthereumAccountIndex];
|
var account = Configuration.CodexDeployment.GethStartResult.MarketplaceNetwork.Bootstrap.AllAccounts.Accounts[EthereumAccountIndex];
|
||||||
var tokenAddress = Configuration.CodexDeployment.GethStartResult.MarketplaceNetwork.Marketplace.TokenAddress;
|
var tokenAddress = Configuration.CodexDeployment.GethStartResult.MarketplaceNetwork.Marketplace.TokenAddress;
|
||||||
|
|
||||||
|
@ -60,21 +66,24 @@ namespace ContinuousTests.Tests
|
||||||
cid = UploadFile(codexAccess.Node, file);
|
cid = UploadFile(codexAccess.Node, file);
|
||||||
Assert.That(cid, Is.Not.Null);
|
Assert.That(cid, Is.Not.Null);
|
||||||
|
|
||||||
startingBalance = marketAccess.GetBalance();
|
var balance = marketAccess.GetBalance();
|
||||||
|
Log.Log("Account: " + account.Account);
|
||||||
|
Log.Log("Balance: " + balance);
|
||||||
|
|
||||||
purchaseId = marketAccess.RequestStorage(
|
purchaseId = marketAccess.RequestStorage(
|
||||||
contentId: cid!,
|
contentId: cid!,
|
||||||
pricePerBytePerSecond: pricePerBytePerSecond,
|
pricePerSlotPerSecond: pricePerSlotPerSecond,
|
||||||
requiredCollateral: 100.TestTokens(),
|
requiredCollateral: 100.TestTokens(),
|
||||||
minRequiredNumberOfNodes: 3,
|
minRequiredNumberOfNodes: numberOfSlots,
|
||||||
proofProbability: 10,
|
proofProbability: 10,
|
||||||
duration: contractDuration);
|
duration: contractDuration);
|
||||||
|
|
||||||
|
Log.Log($"PurchaseId: '{purchaseId}'");
|
||||||
Assert.That(!string.IsNullOrEmpty(purchaseId));
|
Assert.That(!string.IsNullOrEmpty(purchaseId));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
flow.Stop(rc);
|
flow.DeleteTestResources();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,13 +92,17 @@ namespace ContinuousTests.Tests
|
||||||
{
|
{
|
||||||
var (workflowCreator, lifecycle) = CreateFacilities();
|
var (workflowCreator, lifecycle) = CreateFacilities();
|
||||||
var flow = workflowCreator.CreateWorkflow();
|
var flow = workflowCreator.CreateWorkflow();
|
||||||
var startupConfig = new StartupConfig();
|
|
||||||
var codexStartConfig = new CodexStartupConfig(CodexLogLevel.Debug);
|
|
||||||
startupConfig.Add(codexStartConfig);
|
|
||||||
var rc = flow.Start(1, Location.Unspecified, new CodexContainerRecipe(), startupConfig);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var debugInfo = Nodes[0].GetDebugInfo();
|
||||||
|
Assert.That(!string.IsNullOrEmpty(debugInfo.spr));
|
||||||
|
|
||||||
|
var startupConfig = new StartupConfig();
|
||||||
|
var codexStartConfig = new CodexStartupConfig(CodexLogLevel.Debug);
|
||||||
|
codexStartConfig.BootstrapSpr = debugInfo.spr;
|
||||||
|
startupConfig.Add(codexStartConfig);
|
||||||
|
var rc = flow.Start(1, Location.Unspecified, new CodexContainerRecipe(), startupConfig);
|
||||||
var container = rc.Containers[0];
|
var container = rc.Containers[0];
|
||||||
var codexAccess = new CodexAccess(lifecycle, container);
|
var codexAccess = new CodexAccess(lifecycle, container);
|
||||||
|
|
||||||
|
@ -99,32 +112,39 @@ namespace ContinuousTests.Tests
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
flow.Stop(rc);
|
flow.DeleteTestResources();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private (WorkflowCreator, TestLifecycle) CreateFacilities()
|
private (WorkflowCreator, TestLifecycle) CreateFacilities()
|
||||||
{
|
{
|
||||||
|
var kubeConfig = GetKubeConfig(Configuration.KubeConfigFile);
|
||||||
var lifecycleConfig = new DistTestCore.Configuration
|
var lifecycleConfig = new DistTestCore.Configuration
|
||||||
(
|
(
|
||||||
kubeConfigFile: Configuration.KubeConfigFile,
|
kubeConfigFile: kubeConfig,
|
||||||
logPath: "null",
|
logPath: "null",
|
||||||
logDebug: false,
|
logDebug: false,
|
||||||
dataFilesPath: "notUsed",
|
dataFilesPath: Configuration.LogPath,
|
||||||
codexLogLevel: CodexLogLevel.Debug,
|
codexLogLevel: CodexLogLevel.Debug,
|
||||||
runnerLocation: TestRunnerLocation.InternalToCluster
|
runnerLocation: TestRunnerLocation.ExternalToCluster
|
||||||
);
|
);
|
||||||
|
|
||||||
var kubeConfig = new KubernetesWorkflow.Configuration(
|
var kubeFlowConfig = new KubernetesWorkflow.Configuration(
|
||||||
k8sNamespacePrefix: MarketplaceTestNamespace,
|
k8sNamespacePrefix: MarketplaceTestNamespace,
|
||||||
kubeConfigFile: Configuration.KubeConfigFile,
|
kubeConfigFile: kubeConfig,
|
||||||
operationTimeout: TimeSet.K8sOperationTimeout(),
|
operationTimeout: TimeSet.K8sOperationTimeout(),
|
||||||
retryDelay: TimeSet.WaitForK8sServiceDelay());
|
retryDelay: TimeSet.WaitForK8sServiceDelay());
|
||||||
|
|
||||||
var workflowCreator = new WorkflowCreator(Log, kubeConfig, testNamespacePostfix: string.Empty);
|
var workflowCreator = new WorkflowCreator(Log, kubeFlowConfig, testNamespacePostfix: string.Empty);
|
||||||
var lifecycle = new TestLifecycle(new NullLog(), lifecycleConfig, TimeSet, workflowCreator);
|
var lifecycle = new TestLifecycle(new NullLog(), lifecycleConfig, TimeSet, workflowCreator);
|
||||||
|
|
||||||
return (workflowCreator, lifecycle);
|
return (workflowCreator, lifecycle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string? GetKubeConfig(string kubeConfigFile)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(kubeConfigFile) || kubeConfigFile.ToLowerInvariant() == "null") return null;
|
||||||
|
return kubeConfigFile;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,11 @@ namespace DistTestCore.Codex
|
||||||
return Http().HttpPostJson($"storage/request/{contentId}", request);
|
return Http().HttpPostJson($"storage/request/{contentId}", request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CodexStoragePurchase GetPurchaseStatus(string purchaseId)
|
||||||
|
{
|
||||||
|
return Http().HttpGetJson<CodexStoragePurchase>($"storage/purchases/{purchaseId}");
|
||||||
|
}
|
||||||
|
|
||||||
public string ConnectToPeer(string peerId, string peerMultiAddress)
|
public string ConnectToPeer(string peerId, string peerMultiAddress)
|
||||||
{
|
{
|
||||||
return Http().HttpGetString($"connect/{peerId}?addrs={peerMultiAddress}");
|
return Http().HttpGetString($"connect/{peerId}?addrs={peerMultiAddress}");
|
||||||
|
@ -170,4 +175,10 @@ namespace DistTestCore.Codex
|
||||||
public uint? nodes { get; set; }
|
public uint? nodes { get; set; }
|
||||||
public uint? tolerance { get; set; }
|
public uint? tolerance { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CodexStoragePurchase
|
||||||
|
{
|
||||||
|
public string state { get; set; } = string.Empty;
|
||||||
|
public string error { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -250,7 +250,7 @@ namespace DistTestCore
|
||||||
{
|
{
|
||||||
OnEachCodexNode(lifecycle, node =>
|
OnEachCodexNode(lifecycle, node =>
|
||||||
{
|
{
|
||||||
lifecycle.DownloadLog(node);
|
lifecycle.DownloadLog(node.CodexAccess.Container);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,17 +3,17 @@ using NUnit.Framework;
|
||||||
|
|
||||||
namespace DistTestCore.Logs
|
namespace DistTestCore.Logs
|
||||||
{
|
{
|
||||||
public interface ICodexNodeLog
|
public interface IDownloadedLog
|
||||||
{
|
{
|
||||||
void AssertLogContains(string expectedString);
|
void AssertLogContains(string expectedString);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CodexNodeLog : ICodexNodeLog
|
public class DownloadedLog : IDownloadedLog
|
||||||
{
|
{
|
||||||
private readonly LogFile logFile;
|
private readonly LogFile logFile;
|
||||||
private readonly OnlineCodexNode owner;
|
private readonly string owner;
|
||||||
|
|
||||||
public CodexNodeLog(LogFile logFile, OnlineCodexNode owner)
|
public DownloadedLog(LogFile logFile, string owner)
|
||||||
{
|
{
|
||||||
this.logFile = logFile;
|
this.logFile = logFile;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
|
@ -31,7 +31,7 @@ namespace DistTestCore.Logs
|
||||||
line = streamReader.ReadLine();
|
line = streamReader.ReadLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.Fail($"{owner.GetName()} Unable to find string '{expectedString}' in CodexNode log file {logFile.FullFilename}");
|
Assert.Fail($"{owner} Unable to find string '{expectedString}' in CodexNode log file {logFile.FullFilename}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,21 +5,21 @@ namespace DistTestCore.Logs
|
||||||
{
|
{
|
||||||
public class LogDownloadHandler : LogHandler, ILogHandler
|
public class LogDownloadHandler : LogHandler, ILogHandler
|
||||||
{
|
{
|
||||||
private readonly OnlineCodexNode node;
|
private readonly RunningContainer container;
|
||||||
private readonly LogFile log;
|
private readonly LogFile log;
|
||||||
|
|
||||||
public LogDownloadHandler(OnlineCodexNode node, string description, LogFile log)
|
public LogDownloadHandler(RunningContainer container, string description, LogFile log)
|
||||||
{
|
{
|
||||||
this.node = node;
|
this.container = container;
|
||||||
this.log = log;
|
this.log = log;
|
||||||
|
|
||||||
log.Write($"{description} -->> {log.FullFilename}");
|
log.Write($"{description} -->> {log.FullFilename}");
|
||||||
log.WriteRaw(description);
|
log.WriteRaw(description);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CodexNodeLog CreateCodexNodeLog()
|
public DownloadedLog DownloadLog()
|
||||||
{
|
{
|
||||||
return new CodexNodeLog(log, node);
|
return new DownloadedLog(log, container.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ProcessLine(string line)
|
protected override void ProcessLine(string line)
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace DistTestCore.Marketplace
|
||||||
public interface IMarketplaceAccess
|
public interface IMarketplaceAccess
|
||||||
{
|
{
|
||||||
string MakeStorageAvailable(ByteSize size, TestToken minPricePerBytePerSecond, TestToken maxCollateral, TimeSpan maxDuration);
|
string MakeStorageAvailable(ByteSize size, TestToken minPricePerBytePerSecond, TestToken maxCollateral, TimeSpan maxDuration);
|
||||||
string RequestStorage(ContentId contentId, TestToken pricePerBytePerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration);
|
string RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration);
|
||||||
void AssertThatBalance(IResolveConstraint constraint, string message = "");
|
void AssertThatBalance(IResolveConstraint constraint, string message = "");
|
||||||
TestToken GetBalance();
|
TestToken GetBalance();
|
||||||
}
|
}
|
||||||
|
@ -30,13 +30,13 @@ namespace DistTestCore.Marketplace
|
||||||
this.codexAccess = codexAccess;
|
this.codexAccess = codexAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string RequestStorage(ContentId contentId, TestToken pricePerBytePerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration)
|
public string RequestStorage(ContentId contentId, TestToken pricePerSlotPerSecond, TestToken requiredCollateral, uint minRequiredNumberOfNodes, int proofProbability, TimeSpan duration)
|
||||||
{
|
{
|
||||||
var request = new CodexSalesRequestStorageRequest
|
var request = new CodexSalesRequestStorageRequest
|
||||||
{
|
{
|
||||||
duration = ToHexBigInt(duration.TotalSeconds),
|
duration = ToHexBigInt(duration.TotalSeconds),
|
||||||
proofProbability = ToHexBigInt(proofProbability),
|
proofProbability = ToHexBigInt(proofProbability),
|
||||||
reward = ToHexBigInt(pricePerBytePerSecond),
|
reward = ToHexBigInt(pricePerSlotPerSecond),
|
||||||
collateral = ToHexBigInt(requiredCollateral),
|
collateral = ToHexBigInt(requiredCollateral),
|
||||||
expiry = null,
|
expiry = null,
|
||||||
nodes = minRequiredNumberOfNodes,
|
nodes = minRequiredNumberOfNodes,
|
||||||
|
@ -44,7 +44,7 @@ namespace DistTestCore.Marketplace
|
||||||
};
|
};
|
||||||
|
|
||||||
Log($"Requesting storage for: {contentId.Id}... (" +
|
Log($"Requesting storage for: {contentId.Id}... (" +
|
||||||
$"pricePerBytePerSecond: {pricePerBytePerSecond}, " +
|
$"pricePerSlotPerSecond: {pricePerSlotPerSecond}, " +
|
||||||
$"requiredCollateral: {requiredCollateral}, " +
|
$"requiredCollateral: {requiredCollateral}, " +
|
||||||
$"minRequiredNumberOfNodes: {minRequiredNumberOfNodes}, " +
|
$"minRequiredNumberOfNodes: {minRequiredNumberOfNodes}, " +
|
||||||
$"proofProbability: {proofProbability}, " +
|
$"proofProbability: {proofProbability}, " +
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace DistTestCore
|
||||||
ContentId UploadFile(TestFile file);
|
ContentId UploadFile(TestFile file);
|
||||||
TestFile? DownloadContent(ContentId contentId, string fileLabel = "");
|
TestFile? DownloadContent(ContentId contentId, string fileLabel = "");
|
||||||
void ConnectToPeer(IOnlineCodexNode node);
|
void ConnectToPeer(IOnlineCodexNode node);
|
||||||
ICodexNodeLog DownloadLog();
|
IDownloadedLog DownloadLog();
|
||||||
IMetricsAccess Metrics { get; }
|
IMetricsAccess Metrics { get; }
|
||||||
IMarketplaceAccess Marketplace { get; }
|
IMarketplaceAccess Marketplace { get; }
|
||||||
ICodexSetup BringOffline();
|
ICodexSetup BringOffline();
|
||||||
|
@ -107,9 +107,9 @@ namespace DistTestCore
|
||||||
Log($"Successfully connected to peer {peer.GetName()}.");
|
Log($"Successfully connected to peer {peer.GetName()}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICodexNodeLog DownloadLog()
|
public IDownloadedLog DownloadLog()
|
||||||
{
|
{
|
||||||
return lifecycle.DownloadLog(this);
|
return lifecycle.DownloadLog(CodexAccess.Container);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICodexSetup BringOffline()
|
public ICodexSetup BringOffline()
|
||||||
|
|
|
@ -41,16 +41,16 @@ namespace DistTestCore
|
||||||
FileManager.DeleteAllTestFiles();
|
FileManager.DeleteAllTestFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICodexNodeLog DownloadLog(OnlineCodexNode node)
|
public IDownloadedLog DownloadLog(RunningContainer container)
|
||||||
{
|
{
|
||||||
var subFile = Log.CreateSubfile();
|
var subFile = Log.CreateSubfile();
|
||||||
var description = node.GetName();
|
var description = container.Name;
|
||||||
var handler = new LogDownloadHandler(node, description, subFile);
|
var handler = new LogDownloadHandler(container, description, subFile);
|
||||||
|
|
||||||
Log.Log($"Downloading logs for {description} to file '{subFile.FullFilename}'");
|
Log.Log($"Downloading logs for {description} to file '{subFile.FullFilename}'");
|
||||||
CodexStarter.DownloadLog(node.CodexAccess.Container, handler);
|
CodexStarter.DownloadLog(container, handler);
|
||||||
|
|
||||||
return new CodexNodeLog(subFile, node);
|
return new DownloadedLog(subFile, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetTestDuration()
|
public string GetTestDuration()
|
||||||
|
|
|
@ -39,7 +39,7 @@ namespace KubernetesWorkflow
|
||||||
var (serviceName, servicePortsMap) = CreateService(containerRecipes);
|
var (serviceName, servicePortsMap) = CreateService(containerRecipes);
|
||||||
var podInfo = FetchNewPod();
|
var podInfo = FetchNewPod();
|
||||||
|
|
||||||
return new RunningPod(cluster, podInfo, deploymentName, serviceName, servicePortsMap);
|
return new RunningPod(cluster, podInfo, deploymentName, serviceName, servicePortsMap.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop(RunningPod pod)
|
public void Stop(RunningPod pod)
|
||||||
|
@ -436,9 +436,9 @@ namespace KubernetesWorkflow
|
||||||
|
|
||||||
#region Service management
|
#region Service management
|
||||||
|
|
||||||
private (string, Dictionary<ContainerRecipe, Port[]>) CreateService(ContainerRecipe[] containerRecipes)
|
private (string, List<ContainerRecipePortMapEntry>) CreateService(ContainerRecipe[] containerRecipes)
|
||||||
{
|
{
|
||||||
var result = new Dictionary<ContainerRecipe, Port[]>();
|
var result = new List<ContainerRecipePortMapEntry>();
|
||||||
|
|
||||||
var ports = CreateServicePorts(containerRecipes);
|
var ports = CreateServicePorts(containerRecipes);
|
||||||
|
|
||||||
|
@ -468,7 +468,7 @@ namespace KubernetesWorkflow
|
||||||
return (serviceSpec.Metadata.Name, result);
|
return (serviceSpec.Metadata.Name, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReadBackServiceAndMapPorts(V1Service serviceSpec, ContainerRecipe[] containerRecipes, Dictionary<ContainerRecipe, Port[]> result)
|
private void ReadBackServiceAndMapPorts(V1Service serviceSpec, ContainerRecipe[] containerRecipes, List<ContainerRecipePortMapEntry> result)
|
||||||
{
|
{
|
||||||
// For each container-recipe, we need to figure out which service-ports it was assigned by K8s.
|
// For each container-recipe, we need to figure out which service-ports it was assigned by K8s.
|
||||||
var readback = client.Run(c => c.ReadNamespacedService(serviceSpec.Metadata.Name, K8sTestNamespace));
|
var readback = client.Run(c => c.ReadNamespacedService(serviceSpec.Metadata.Name, K8sTestNamespace));
|
||||||
|
@ -485,7 +485,8 @@ namespace KubernetesWorkflow
|
||||||
// These service ports belongs to this recipe.
|
// These service ports belongs to this recipe.
|
||||||
var optionals = matchingServicePorts.Select(p => MapNodePortIfAble(p, portName));
|
var optionals = matchingServicePorts.Select(p => MapNodePortIfAble(p, portName));
|
||||||
var ports = optionals.Where(p => p != null).Select(p => p!).ToArray();
|
var ports = optionals.Where(p => p != null).Select(p => p!).ToArray();
|
||||||
result.Add(r, ports);
|
|
||||||
|
result.Add(new ContainerRecipePortMapEntry(r.Number, ports));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,35 +2,50 @@
|
||||||
{
|
{
|
||||||
public class RunningPod
|
public class RunningPod
|
||||||
{
|
{
|
||||||
private readonly Dictionary<ContainerRecipe, Port[]> servicePortMap;
|
public RunningPod(K8sCluster cluster, PodInfo podInfo, string deploymentName, string serviceName, ContainerRecipePortMapEntry[] portMapEntries)
|
||||||
|
|
||||||
public RunningPod(K8sCluster cluster, PodInfo podInfo, string deploymentName, string serviceName, Dictionary<ContainerRecipe, Port[]> servicePortMap)
|
|
||||||
{
|
{
|
||||||
Cluster = cluster;
|
Cluster = cluster;
|
||||||
PodInfo = podInfo;
|
PodInfo = podInfo;
|
||||||
DeploymentName = deploymentName;
|
DeploymentName = deploymentName;
|
||||||
ServiceName = serviceName;
|
ServiceName = serviceName;
|
||||||
this.servicePortMap = servicePortMap;
|
PortMapEntries = portMapEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
public K8sCluster Cluster { get; }
|
public K8sCluster Cluster { get; }
|
||||||
public PodInfo PodInfo { get; }
|
public PodInfo PodInfo { get; }
|
||||||
|
public ContainerRecipePortMapEntry[] PortMapEntries { get; }
|
||||||
internal string DeploymentName { get; }
|
internal string DeploymentName { get; }
|
||||||
internal string ServiceName { get; }
|
internal string ServiceName { get; }
|
||||||
|
|
||||||
public Port[] GetServicePortsForContainerRecipe(ContainerRecipe containerRecipe)
|
public Port[] GetServicePortsForContainerRecipe(ContainerRecipe containerRecipe)
|
||||||
{
|
{
|
||||||
if (!servicePortMap.ContainsKey(containerRecipe)) return Array.Empty<Port>();
|
if (PortMapEntries.Any(p => p.ContainerNumber == containerRecipe.Number))
|
||||||
return servicePortMap[containerRecipe];
|
{
|
||||||
|
return PortMapEntries.Single(p => p.ContainerNumber == containerRecipe.Number).Ports;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Array.Empty<Port>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ContainerRecipePortMapEntry
|
||||||
|
{
|
||||||
|
public ContainerRecipePortMapEntry(int containerNumber, Port[] ports)
|
||||||
|
{
|
||||||
|
ContainerNumber = containerNumber;
|
||||||
|
Ports = ports;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ContainerNumber { get; }
|
||||||
|
public Port[] Ports { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PodInfo
|
public class PodInfo
|
||||||
{
|
{
|
||||||
public PodInfo(string podName, string podIp, string k8sNodeName)
|
public PodInfo(string name, string ip, string k8sNodeName)
|
||||||
{
|
{
|
||||||
Name = podName;
|
Name = name;
|
||||||
Ip = podIp;
|
Ip = ip;
|
||||||
K8SNodeName = k8sNodeName;
|
K8SNodeName = k8sNodeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ namespace Tests.BasicTests
|
||||||
|
|
||||||
var contentId = buyer.UploadFile(testFile);
|
var contentId = buyer.UploadFile(testFile);
|
||||||
buyer.Marketplace.RequestStorage(contentId,
|
buyer.Marketplace.RequestStorage(contentId,
|
||||||
pricePerBytePerSecond: 2.TestTokens(),
|
pricePerSlotPerSecond: 2.TestTokens(),
|
||||||
requiredCollateral: 10.TestTokens(),
|
requiredCollateral: 10.TestTokens(),
|
||||||
minRequiredNumberOfNodes: 1,
|
minRequiredNumberOfNodes: 1,
|
||||||
proofProbability: 5,
|
proofProbability: 5,
|
||||||
|
|
Loading…
Reference in New Issue