OneClient test passed

This commit is contained in:
benbierens 2023-04-13 09:33:10 +02:00
parent bb81d7f037
commit f5c60f0bca
No known key found for this signature in database
GPG Key ID: FE44815D96D0A1AA
12 changed files with 379 additions and 156 deletions

View File

@ -4,13 +4,13 @@ namespace DistTestCore.Codex
{
public class CodexAccess
{
private readonly RunningContainer runningContainer;
public CodexAccess(RunningContainer runningContainer)
{
this.runningContainer = runningContainer;
Container = runningContainer;
}
public RunningContainer Container { get; }
public CodexDebugResponse GetDebugInfo()
{
var response = Http().HttpGetJson<CodexDebugResponse>("debug/info");
@ -18,12 +18,27 @@ namespace DistTestCore.Codex
return response;
}
public string UploadFile(FileStream fileStream)
{
return Http().HttpPostStream("upload", fileStream);
}
public Stream DownloadFile(string contentId)
{
return Http().HttpGetStream("download/" + contentId);
}
private Http Http()
{
var ip = runningContainer.Pod.Cluster.GetIp();
var port = runningContainer.ServicePorts[0].Number;
var ip = Container.Pod.Cluster.GetIp();
var port = Container.ServicePorts[0].Number;
return new Http(ip, port, baseUrl: "/api/codex/v1");
}
public string ConnectToPeer(string peerId, string peerMultiAddress)
{
return Http().HttpGetString($"connect/{peerId}?addrs={peerMultiAddress}");
}
}
public class CodexDebugResponse

View File

@ -1,4 +1,6 @@
namespace DistTestCore.Codex
using KubernetesWorkflow;
namespace DistTestCore.Codex
{
public class CodexStartupConfig
{

View File

@ -0,0 +1,78 @@
using DistTestCore.Codex;
using KubernetesWorkflow;
using System.Collections;
namespace DistTestCore
{
public interface ICodexNodeGroup : IEnumerable<IOnlineCodexNode>
{
//ICodexSetup BringOffline();
IOnlineCodexNode this[int index] { get; }
}
public class CodexNodeGroup : ICodexNodeGroup
{
private readonly TestLifecycle lifecycle;
public CodexNodeGroup(TestLifecycle lifecycle, CodexSetup setup, RunningContainers containers)
{
this.lifecycle = lifecycle;
Setup = setup;
Containers = containers;
Nodes = containers.Containers.Select(c => CreateOnlineCodexNode(c)).ToArray();
}
public IOnlineCodexNode this[int index]
{
get
{
return Nodes[index];
}
}
//public ICodexSetup BringOffline()
//{
// //return k8SManager.BringOffline(this);
//}
public CodexSetup Setup { get; }
public RunningContainers Containers { get; }
public OnlineCodexNode[] Nodes { get; }
//public GethCompanionGroup? GethCompanionGroup { get; set; }
//public CodexNodeContainer[] GetContainers()
//{
// return Nodes.Select(n => n.Container).ToArray();
//}
public IEnumerator<IOnlineCodexNode> GetEnumerator()
{
return Nodes.Cast<IOnlineCodexNode>().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return Nodes.GetEnumerator();
}
//public CodexNodeLog DownloadLog(IOnlineCodexNode node)
//{
// var logDownloader = new PodLogDownloader(log, k8SManager);
// var n = (OnlineCodexNode)node;
// return logDownloader.DownloadLog(n);
//}
public string Describe()
{
var orderNumber = Containers.RunningPod.Ip;
return $"CodexNodeGroup@{orderNumber}-{Setup.Describe()}";
}
private OnlineCodexNode CreateOnlineCodexNode(RunningContainer c)
{
var access = new CodexAccess(c);
return new OnlineCodexNode(lifecycle, access, this);
}
}
}

View File

@ -3,24 +3,24 @@ using KubernetesWorkflow;
namespace DistTestCore
{
public interface ICodexSetupConfig
public interface ICodexSetup
{
ICodexSetupConfig At(Location location);
ICodexSetupConfig WithLogLevel(CodexLogLevel level);
ICodexSetup At(Location location);
ICodexSetup WithLogLevel(CodexLogLevel level);
//ICodexStartupConfig WithBootstrapNode(IOnlineCodexNode node);
ICodexSetupConfig WithStorageQuota(ByteSize storageQuota);
ICodexSetupConfig EnableMetrics();
ICodexSetup WithStorageQuota(ByteSize storageQuota);
ICodexSetup EnableMetrics();
//ICodexSetupConfig EnableMarketplace(int initialBalance);
ICodexNodeGroup BringOnline();
}
public class CodexSetupConfig : CodexStartupConfig, ICodexSetupConfig
public class CodexSetup : CodexStartupConfig, ICodexSetup
{
private readonly CodexStarter starter;
public int NumberOfNodes { get; }
public CodexSetupConfig(CodexStarter starter, int numberOfNodes)
public CodexSetup(CodexStarter starter, int numberOfNodes)
{
this.starter = starter;
NumberOfNodes = numberOfNodes;
@ -31,7 +31,7 @@ namespace DistTestCore
return starter.BringOnline(this);
}
public ICodexSetupConfig At(Location location)
public ICodexSetup At(Location location)
{
Location = location;
return this;
@ -43,19 +43,19 @@ namespace DistTestCore
// return this;
//}
public ICodexSetupConfig WithLogLevel(CodexLogLevel level)
public ICodexSetup WithLogLevel(CodexLogLevel level)
{
LogLevel = level;
return this;
}
public ICodexSetupConfig WithStorageQuota(ByteSize storageQuota)
public ICodexSetup WithStorageQuota(ByteSize storageQuota)
{
StorageQuota = storageQuota;
return this;
}
public ICodexSetupConfig EnableMetrics()
public ICodexSetup EnableMetrics()
{
MetricsEnabled = true;
return this;

View File

@ -1,27 +1,28 @@
using DistTestCore.Codex;
using KubernetesWorkflow;
using Logging;
namespace DistTestCore
{
public class CodexStarter
{
private readonly WorkflowCreator workflowCreator;
private readonly TestLifecycle lifecycle;
public CodexStarter(TestLog log, Configuration configuration)
public CodexStarter(TestLifecycle lifecycle, Configuration configuration)
{
workflowCreator = new WorkflowCreator(configuration.GetK8sConfiguration());
this.lifecycle = lifecycle;
}
public ICodexNodeGroup BringOnline(CodexSetupConfig codexSetupConfig)
public ICodexNodeGroup BringOnline(CodexSetup codexSetup)
{
var workflow = workflowCreator.CreateWorkflow();
var startupConfig = new StartupConfig();
startupConfig.Add(codexSetupConfig);
startupConfig.Add(codexSetup);
var runningContainers = workflow.Start(codexSetupConfig.NumberOfNodes, codexSetupConfig.Location, new CodexContainerRecipe(), startupConfig);
var runningContainers = workflow.Start(codexSetup.NumberOfNodes, codexSetup.Location, new CodexContainerRecipe(), startupConfig);
// create access objects. Easy, right?
return new CodexNodeGroup(lifecycle, codexSetup, runningContainers);
}
public void DeleteAllResources()

View File

@ -61,27 +61,27 @@ namespace DistTestCore
return lifecycle.FileManager.GenerateTestFile(size);
}
public ICodexSetupConfig SetupCodexNodes(int numberOfNodes)
public ICodexSetup SetupCodexNodes(int numberOfNodes)
{
return new CodexSetupConfig(lifecycle.CodexStarter, numberOfNodes);
return new CodexSetup(lifecycle.CodexStarter, numberOfNodes);
}
private void IncludeLogsAndMetricsOnTestFailure()
{
var result = TestContext.CurrentContext.Result;
if (result.Outcome.Status == NUnit.Framework.Interfaces.TestStatus.Failed)
{
if (IsDownloadingLogsAndMetricsEnabled())
{
log.Log("Downloading all CodexNode logs and metrics because of test failure...");
k8sManager.ForEachOnlineGroup(DownloadLogs);
k8sManager.DownloadAllMetrics();
}
else
{
log.Log("Skipping download of all CodexNode logs and metrics due to [DontDownloadLogsAndMetricsOnFailure] attribute.");
}
}
//var result = TestContext.CurrentContext.Result;
//if (result.Outcome.Status == NUnit.Framework.Interfaces.TestStatus.Failed)
//{
// if (IsDownloadingLogsAndMetricsEnabled())
// {
// log.Log("Downloading all CodexNode logs and metrics because of test failure...");
// k8sManager.ForEachOnlineGroup(DownloadLogs);
// k8sManager.DownloadAllMetrics();
// }
// else
// {
// log.Log("Skipping download of all CodexNode logs and metrics due to [DontDownloadLogsAndMetricsOnFailure] attribute.");
// }
//}
}
private void Log(string msg)
@ -101,19 +101,19 @@ namespace DistTestCore
private void DownloadLogs(CodexNodeGroup group)
{
foreach (var node in group)
{
var downloader = new PodLogDownloader(log, k8sManager);
var n = (OnlineCodexNode)node;
downloader.DownloadLog(n);
}
//foreach (var node in group)
//{
// var downloader = new PodLogDownloader(log, k8sManager);
// var n = (OnlineCodexNode)node;
// downloader.DownloadLog(n);
//}
}
private bool IsDownloadingLogsAndMetricsEnabled()
{
var testProperties = TestContext.CurrentContext.Test.Properties;
return !testProperties.ContainsKey(PodLogDownloader.DontDownloadLogsOnFailureKey);
}
//private bool IsDownloadingLogsAndMetricsEnabled()
//{
// var testProperties = TestContext.CurrentContext.Test.Properties;
// return !testProperties.ContainsKey(PodLogDownloader.DontDownloadLogsOnFailureKey);
//}
}
public static class GlobalTestFailure

View File

@ -1,6 +1,7 @@
using Newtonsoft.Json;
using NUnit.Framework;
using System.Net.Http.Headers;
using Utils;
namespace DistTestCore
{
@ -26,8 +27,8 @@ namespace DistTestCore
{
using var client = GetClient();
var url = GetUrl() + route;
var result = Utils.Wait(client.GetAsync(url));
return Utils.Wait(result.Content.ReadAsStringAsync());
var result = Time.Wait(client.GetAsync(url));
return Time.Wait(result.Content.ReadAsStringAsync());
});
}
@ -45,9 +46,9 @@ namespace DistTestCore
var content = new StreamContent(stream);
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
var response = Utils.Wait(client.PostAsync(url, content));
var response = Time.Wait(client.PostAsync(url, content));
return Utils.Wait(response.Content.ReadAsStringAsync());
return Time.Wait(response.Content.ReadAsStringAsync());
});
}
@ -58,7 +59,7 @@ namespace DistTestCore
var client = GetClient();
var url = GetUrl() + route;
return Utils.Wait(client.GetStreamAsync(url));
return Time.Wait(client.GetStreamAsync(url));
});
}

View File

@ -0,0 +1,126 @@
using DistTestCore.Codex;
using NUnit.Framework;
namespace DistTestCore
{
public interface IOnlineCodexNode
{
CodexDebugResponse GetDebugInfo();
ContentId UploadFile(TestFile file);
TestFile? DownloadContent(ContentId contentId);
void ConnectToPeer(IOnlineCodexNode node);
//ICodexNodeLog DownloadLog();
//IMetricsAccess Metrics { get; }
//IMarketplaceAccess Marketplace { get; }
}
public class OnlineCodexNode : IOnlineCodexNode
{
private const string SuccessfullyConnectedMessage = "Successfully connected to peer";
private const string UploadFailedMessage = "Unable to store block";
private readonly TestLifecycle lifecycle;
public OnlineCodexNode(TestLifecycle lifecycle, CodexAccess codexAccess, CodexNodeGroup group)
{
this.lifecycle = lifecycle;
CodexAccess = codexAccess;
Group = group;
}
public CodexAccess CodexAccess { get; }
public CodexNodeGroup Group { get; }
public string GetName()
{
return $"<{CodexAccess.Container.Recipe.Name}>";
}
public CodexDebugResponse GetDebugInfo()
{
var response = CodexAccess.GetDebugInfo();
Log($"Got DebugInfo with id: '{response.id}'.");
return response;
}
public ContentId UploadFile(TestFile file)
{
Log($"Uploading file of size {file.GetFileSize()}...");
using var fileStream = File.OpenRead(file.Filename);
var response = CodexAccess.UploadFile(fileStream);
if (response.StartsWith(UploadFailedMessage))
{
Assert.Fail("Node failed to store block.");
}
Log($"Uploaded file. Received contentId: '{response}'.");
return new ContentId(response);
}
public TestFile? DownloadContent(ContentId contentId)
{
Log($"Downloading for contentId: '{contentId.Id}'...");
var file = lifecycle.FileManager.CreateEmptyTestFile();
DownloadToFile(contentId.Id, file);
Log($"Downloaded file of size {file.GetFileSize()} to '{file.Filename}'.");
return file;
}
public void ConnectToPeer(IOnlineCodexNode node)
{
var peer = (OnlineCodexNode)node;
Log($"Connecting to peer {peer.GetName()}...");
var peerInfo = node.GetDebugInfo();
var response = CodexAccess.ConnectToPeer(peerInfo.id, GetPeerMultiAddress(peer, peerInfo));
Assert.That(response, Is.EqualTo(SuccessfullyConnectedMessage), "Unable to connect codex nodes.");
Log($"Successfully connected to peer {peer.GetName()}.");
}
//public ICodexNodeLog DownloadLog()
//{
// return Group.DownloadLog(this);
//}
public string Describe()
{
return $"{Group.Describe()} contains {GetName()}";
}
private string GetPeerMultiAddress(OnlineCodexNode peer, CodexDebugResponse peerInfo)
{
var multiAddress = peerInfo.addrs.First();
// Todo: Is there a case where First address in list is not the way?
if (Group == peer.Group)
{
return multiAddress;
}
// The peer we want to connect is in a different pod.
// We must replace the default IP with the pod IP in the multiAddress.
return multiAddress.Replace("0.0.0.0", peer.Group.Containers.RunningPod.Ip);
}
private void DownloadToFile(string contentId, TestFile file)
{
using var fileStream = File.OpenWrite(file.Filename);
using var downloadStream = CodexAccess.DownloadFile(contentId);
downloadStream.CopyTo(fileStream);
}
private void Log(string msg)
{
lifecycle.Log.Log($"{GetName()}: {msg}");
}
}
public class ContentId
{
public ContentId(string id)
{
Id = id;
}
public string Id { get; }
}
}

View File

@ -8,7 +8,7 @@ namespace DistTestCore
{
Log = new TestLog(configuration.GetLogConfig());
FileManager = new FileManager(Log, configuration);
CodexStarter = new CodexStarter(Log, configuration);
CodexStarter = new CodexStarter(this, configuration);
}
public TestLog Log { get; }

View File

@ -187,7 +187,7 @@ namespace KubernetesWorkflow
private string GetNameForPort(ContainerRecipe recipe, Port port)
{
return $"P{workflowNumberSource.WorkflowNumber}-{recipe.Number}-{port.Number}";
return $"p{workflowNumberSource.WorkflowNumber}-{recipe.Number}-{port.Number}";
}
#endregion

View File

@ -1,4 +1,4 @@
using CodexDistTestCore;
using DistTestCore;
using NUnit.Framework;
namespace Tests.BasicTests
@ -6,68 +6,6 @@ namespace Tests.BasicTests
[TestFixture]
public class SimpleTests : DistTest
{
[Test]
public void TwoMetricsExample()
{
var group = SetupCodexNodes(2)
.EnableMetrics()
.BringOnline();
var group2 = SetupCodexNodes(2)
.EnableMetrics()
.BringOnline();
var primary = group[0];
var secondary = group[1];
var primary2 = group2[0];
var secondary2 = group2[1];
primary.ConnectToPeer(secondary);
primary2.ConnectToPeer(secondary2);
Thread.Sleep(TimeSpan.FromMinutes(5));
primary.Metrics.AssertThat("libp2p_peers", Is.EqualTo(1));
primary2.Metrics.AssertThat("libp2p_peers", Is.EqualTo(1));
}
[Test]
public void MarketplaceExample()
{
var group = SetupCodexNodes(4)
.WithStorageQuota(10.GB())
.EnableMarketplace(initialBalance: 20)
.BringOnline();
foreach (var node in group)
{
Assert.That(node.Marketplace.GetBalance(), Is.EqualTo(20));
}
// WIP: Balance is now only ETH.
// todo: All nodes should have plenty of ETH to pay for transactions.
// todo: Upload our own token, use this exclusively. ETH should be invisibile to the tests.
//var secondary = SetupCodexNodes(1)
// .EnableMarketplace(initialBalance: 1000)
// .BringOnline()[0];
//primary.ConnectToPeer(secondary);
//primary.Marketplace.MakeStorageAvailable(10.GB(), minPricePerBytePerSecond: 1, maxCollateral: 20);
//var testFile = GenerateTestFile(10.MB());
//var contentId = secondary.UploadFile(testFile);
//secondary.Marketplace.RequestStorage(contentId, pricePerBytePerSecond: 2,
// requiredCollateral: 10, minRequiredNumberOfNodes: 1);
//primary.Marketplace.AssertThatBalance(Is.LessThan(20), "Collateral was not placed.");
//var primaryBalance = primary.Marketplace.GetBalance();
//secondary.Marketplace.AssertThatBalance(Is.LessThan(1000), "Contractor was not charged for storage.");
//primary.Marketplace.AssertThatBalance(Is.GreaterThan(primaryBalance), "Storer was not paid for storage.");
}
[Test]
public void OneClientTest()
{
@ -82,53 +20,115 @@ namespace Tests.BasicTests
testFile.AssertIsEqual(downloadedFile);
}
[Test]
public void TwoClientsOnePodTest()
{
var group = SetupCodexNodes(2).BringOnline();
//[Test]
//public void TwoClientsOnePodTest()
//{
// var group = SetupCodexNodes(2).BringOnline();
var primary = group[0];
var secondary = group[1];
// var primary = group[0];
// var secondary = group[1];
PerformTwoClientTest(primary, secondary);
}
// PerformTwoClientTest(primary, secondary);
//}
[Test]
public void TwoClientsTwoPodsTest()
{
var primary = SetupCodexNodes(1).BringOnline()[0];
//[Test]
//public void TwoClientsTwoPodsTest()
//{
// var primary = SetupCodexNodes(1).BringOnline()[0];
var secondary = SetupCodexNodes(1).BringOnline()[0];
// var secondary = SetupCodexNodes(1).BringOnline()[0];
PerformTwoClientTest(primary, secondary);
}
// PerformTwoClientTest(primary, secondary);
//}
[Test]
[Ignore("Requires Location map to be configured for k8s cluster.")]
public void TwoClientsTwoLocationsTest()
{
var primary = SetupCodexNodes(1)
.At(Location.BensLaptop)
.BringOnline()[0];
//[Test]
//[Ignore("Requires Location map to be configured for k8s cluster.")]
//public void TwoClientsTwoLocationsTest()
//{
// var primary = SetupCodexNodes(1)
// .At(Location.BensLaptop)
// .BringOnline()[0];
var secondary = SetupCodexNodes(1)
.At(Location.BensOldGamingMachine)
.BringOnline()[0];
// var secondary = SetupCodexNodes(1)
// .At(Location.BensOldGamingMachine)
// .BringOnline()[0];
PerformTwoClientTest(primary, secondary);
}
// PerformTwoClientTest(primary, secondary);
//}
private void PerformTwoClientTest(IOnlineCodexNode primary, IOnlineCodexNode secondary)
{
primary.ConnectToPeer(secondary);
//[Test]
//public void TwoMetricsExample()
//{
// var group = SetupCodexNodes(2)
// .EnableMetrics()
// .BringOnline();
var testFile = GenerateTestFile(1.MB());
// var group2 = SetupCodexNodes(2)
// .EnableMetrics()
// .BringOnline();
var contentId = primary.UploadFile(testFile);
// var primary = group[0];
// var secondary = group[1];
// var primary2 = group2[0];
// var secondary2 = group2[1];
var downloadedFile = secondary.DownloadContent(contentId);
// primary.ConnectToPeer(secondary);
// primary2.ConnectToPeer(secondary2);
testFile.AssertIsEqual(downloadedFile);
}
// Thread.Sleep(TimeSpan.FromMinutes(5));
// primary.Metrics.AssertThat("libp2p_peers", Is.EqualTo(1));
// primary2.Metrics.AssertThat("libp2p_peers", Is.EqualTo(1));
//}
//[Test]
//public void MarketplaceExample()
//{
// var group = SetupCodexNodes(4)
// .WithStorageQuota(10.GB())
// .EnableMarketplace(initialBalance: 20)
// .BringOnline();
// foreach (var node in group)
// {
// Assert.That(node.Marketplace.GetBalance(), Is.EqualTo(20));
// }
// // WIP: Balance is now only ETH.
// // todo: All nodes should have plenty of ETH to pay for transactions.
// // todo: Upload our own token, use this exclusively. ETH should be invisibile to the tests.
// //var secondary = SetupCodexNodes(1)
// // .EnableMarketplace(initialBalance: 1000)
// // .BringOnline()[0];
// //primary.ConnectToPeer(secondary);
// //primary.Marketplace.MakeStorageAvailable(10.GB(), minPricePerBytePerSecond: 1, maxCollateral: 20);
// //var testFile = GenerateTestFile(10.MB());
// //var contentId = secondary.UploadFile(testFile);
// //secondary.Marketplace.RequestStorage(contentId, pricePerBytePerSecond: 2,
// // requiredCollateral: 10, minRequiredNumberOfNodes: 1);
// //primary.Marketplace.AssertThatBalance(Is.LessThan(20), "Collateral was not placed.");
// //var primaryBalance = primary.Marketplace.GetBalance();
// //secondary.Marketplace.AssertThatBalance(Is.LessThan(1000), "Contractor was not charged for storage.");
// //primary.Marketplace.AssertThatBalance(Is.GreaterThan(primaryBalance), "Storer was not paid for storage.");
//}
//private void PerformTwoClientTest(IOnlineCodexNode primary, IOnlineCodexNode secondary)
//{
// primary.ConnectToPeer(secondary);
// var testFile = GenerateTestFile(1.MB());
// var contentId = primary.UploadFile(testFile);
// var downloadedFile = secondary.DownloadContent(contentId);
// testFile.AssertIsEqual(downloadedFile);
//}
}
}

View File

@ -13,7 +13,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CodexDistTestCore\CodexDistTestCore.csproj" />
<ProjectReference Include="..\DistTestCore\DistTestCore.csproj" />
</ItemGroup>
</Project>