mirror of
https://github.com/logos-storage/logos-storage-nim-cs-dist-tests.git
synced 2026-01-09 08:53:13 +00:00
wip
This commit is contained in:
parent
9a227b3d0e
commit
1a8b7b79ef
@ -14,6 +14,12 @@ namespace FileUtils
|
||||
Label = label;
|
||||
}
|
||||
|
||||
public static TrackedFile FromPath(ILog log, string filepath)
|
||||
{
|
||||
// todo: I don't wanne have to do this to call upload.
|
||||
return new TrackedFile(log, filepath, string.Empty);
|
||||
}
|
||||
|
||||
public string Filename { get; }
|
||||
public string Label { get; }
|
||||
|
||||
|
||||
@ -22,5 +22,21 @@
|
||||
{
|
||||
return !string.IsNullOrEmpty(Host) && Port > 0;
|
||||
}
|
||||
|
||||
public static Address Empty()
|
||||
{
|
||||
return new Address(string.Empty, string.Empty, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IHasMetricsScrapeTarget
|
||||
{
|
||||
Address GetMetricsScrapeTarget();
|
||||
}
|
||||
|
||||
public interface IHasManyMetricScrapeTargets
|
||||
{
|
||||
Address[] GetMetricsScrapeTargets();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using CodexOpenApi;
|
||||
using Logging;
|
||||
using Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Utils;
|
||||
using WebUtils;
|
||||
|
||||
@ -36,5 +36,19 @@ namespace CodexClient
|
||||
public Address ListenEndpoint { get; }
|
||||
public EthAccount? EthAccount { get; }
|
||||
public Address? MetricsEndpoint { get; }
|
||||
|
||||
public static ICodexInstance CreateFromApiEndpoint(string name, Address apiEndpoint)
|
||||
{
|
||||
return new CodexInstance(
|
||||
name,
|
||||
imageName: "-",
|
||||
startUtc: DateTime.UtcNow,
|
||||
discoveryEndpoint: Address.Empty(),
|
||||
apiEndpoint: apiEndpoint,
|
||||
listenEndpoint: Address.Empty(),
|
||||
ethAccount: null,
|
||||
metricsEndpoint: null
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ using Utils;
|
||||
|
||||
namespace CodexClient
|
||||
{
|
||||
public partial interface ICodexNode : IHasEthAddress
|
||||
public partial interface ICodexNode : IHasEthAddress, IHasMetricsScrapeTarget
|
||||
{
|
||||
string GetName();
|
||||
string GetImageName();
|
||||
@ -30,11 +30,11 @@ namespace CodexClient
|
||||
IMarketplaceAccess Marketplace { get; }
|
||||
ITransferSpeeds TransferSpeeds { get; }
|
||||
EthAccount EthAccount { get; }
|
||||
StoragePurchase GetPurchaseStatus(string purchaseId);
|
||||
|
||||
Address GetDiscoveryEndpoint();
|
||||
Address GetApiEndpoint();
|
||||
Address GetListenEndpoint();
|
||||
Address GetMetricsScrapeTarget();
|
||||
|
||||
/// <summary>
|
||||
/// Warning! The node is not usable after this.
|
||||
@ -86,6 +86,11 @@ namespace CodexClient
|
||||
public DebugInfoVersion Version { get; private set; }
|
||||
public ITransferSpeeds TransferSpeeds { get => transferSpeeds; }
|
||||
|
||||
public StoragePurchase GetPurchaseStatus(string purchaseId)
|
||||
{
|
||||
return codexAccess.GetPurchaseStatus(purchaseId);
|
||||
}
|
||||
|
||||
public EthAddress EthAddress
|
||||
{
|
||||
get
|
||||
|
||||
@ -9,24 +9,29 @@ namespace CodexClient
|
||||
{
|
||||
private readonly ILog log;
|
||||
private readonly IFileManager fileManager;
|
||||
private readonly CodexHooksFactory hooksFactor;
|
||||
private readonly CodexHooksFactory hooksFactory;
|
||||
private readonly IHttpFactory httpFactory;
|
||||
private readonly IIProcessControlFactory processControlFactory;
|
||||
private readonly IProcessControlFactory processControlFactory;
|
||||
|
||||
public CodexNodeFactory(ILog log, IFileManager fileManager, CodexHooksFactory hooksFactory, IHttpFactory httpFactory, IIProcessControlFactory processControlFactory)
|
||||
public CodexNodeFactory(ILog log, IFileManager fileManager, CodexHooksFactory hooksFactory, IHttpFactory httpFactory, IProcessControlFactory processControlFactory)
|
||||
{
|
||||
this.log = log;
|
||||
this.fileManager = fileManager;
|
||||
this.hooksFactor = hooksFactory;
|
||||
this.hooksFactory = hooksFactory;
|
||||
this.httpFactory = httpFactory;
|
||||
this.processControlFactory = processControlFactory;
|
||||
}
|
||||
|
||||
public CodexNodeFactory(ILog log, string dataDir)
|
||||
: this(log, new FileManager(log, dataDir), new CodexHooksFactory(), new HttpFactory(log), new DoNothingProcessControlFactory())
|
||||
{
|
||||
}
|
||||
|
||||
public ICodexNode CreateCodexNode(ICodexInstance instance)
|
||||
{
|
||||
var processControl = processControlFactory.CreateProcessControl(instance);
|
||||
var access = new CodexAccess(log, httpFactory, processControl, instance);
|
||||
var hooks = hooksFactor.CreateHooks(access.GetName());
|
||||
var hooks = hooksFactory.CreateHooks(access.GetName());
|
||||
var marketplaceAccess = CreateMarketplaceAccess(instance, access, hooks);
|
||||
var node = new CodexNode(log, access, fileManager, marketplaceAccess, hooks);
|
||||
node.Initialize();
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
namespace CodexClient
|
||||
{
|
||||
public interface IIProcessControlFactory
|
||||
public interface IProcessControlFactory
|
||||
{
|
||||
IProcessControl CreateProcessControl(ICodexInstance instance);
|
||||
}
|
||||
@ -14,4 +14,33 @@ namespace CodexClient
|
||||
void DeleteDataDirFolder();
|
||||
bool HasCrashed();
|
||||
}
|
||||
|
||||
public class DoNothingProcessControlFactory : IProcessControlFactory
|
||||
{
|
||||
public IProcessControl CreateProcessControl(ICodexInstance instance)
|
||||
{
|
||||
return new DoNothingProcessControl();
|
||||
}
|
||||
}
|
||||
|
||||
public class DoNothingProcessControl : IProcessControl
|
||||
{
|
||||
public void DeleteDataDirFolder()
|
||||
{
|
||||
}
|
||||
|
||||
public IDownloadedLog DownloadLog(LogFile file)
|
||||
{
|
||||
throw new NotImplementedException("Not supported by DoNothingProcessControl");
|
||||
}
|
||||
|
||||
public bool HasCrashed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Stop(bool waitTillStopped)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ namespace CodexClient
|
||||
void WaitForStorageContractStarted();
|
||||
void WaitForStorageContractFinished();
|
||||
void WaitForContractFailed();
|
||||
StoragePurchase GetPurchaseStatus();
|
||||
}
|
||||
|
||||
public class StoragePurchaseContract : IStoragePurchaseContract
|
||||
@ -88,9 +89,9 @@ namespace CodexClient
|
||||
WaitForStorageContractState(timeout, "failed");
|
||||
}
|
||||
|
||||
public StoragePurchase GetPurchaseStatus(string purchaseId)
|
||||
public StoragePurchase GetPurchaseStatus()
|
||||
{
|
||||
return codexAccess.GetPurchaseStatus(purchaseId);
|
||||
return codexAccess.GetPurchaseStatus(PurchaseId);
|
||||
}
|
||||
|
||||
private void WaitForStorageContractState(TimeSpan timeout, string desiredState, int sleep = 1000)
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
using CodexClient;
|
||||
using Core;
|
||||
using MetricsPlugin;
|
||||
using System.Collections;
|
||||
using Utils;
|
||||
|
||||
@ -42,7 +41,11 @@ namespace CodexPlugin
|
||||
|
||||
public ICodexNode[] Nodes => nodes;
|
||||
public DebugInfoVersion Version { get; private set; }
|
||||
public Address[] ScrapeTargets => Nodes.Select(n => n.GetMetricsScrapeTarget()).ToArray();
|
||||
|
||||
public Address[] GetMetricsScrapeTargets()
|
||||
{
|
||||
return Nodes.Select(n => n.GetMetricsScrapeTarget()).ToArray();
|
||||
}
|
||||
|
||||
public IEnumerator<ICodexNode> GetEnumerator()
|
||||
{
|
||||
|
||||
@ -8,7 +8,7 @@ using Utils;
|
||||
|
||||
namespace CodexPlugin
|
||||
{
|
||||
public class CodexStarter : IIProcessControlFactory
|
||||
public class CodexStarter : IProcessControlFactory
|
||||
{
|
||||
private readonly IPluginTools pluginTools;
|
||||
private readonly CodexContainerRecipe recipe = new CodexContainerRecipe();
|
||||
|
||||
@ -9,7 +9,7 @@ namespace MetricsPlugin
|
||||
{
|
||||
public static RunningPod DeployMetricsCollector(this CoreInterface ci, TimeSpan scrapeInterval, params IHasMetricsScrapeTarget[] scrapeTargets)
|
||||
{
|
||||
return Plugin(ci).DeployMetricsCollector(scrapeTargets.Select(t => t.MetricsScrapeTarget).ToArray(), scrapeInterval);
|
||||
return Plugin(ci).DeployMetricsCollector(scrapeTargets.Select(t => t.GetMetricsScrapeTarget()).ToArray(), scrapeInterval);
|
||||
}
|
||||
|
||||
public static RunningPod DeployMetricsCollector(this CoreInterface ci, TimeSpan scrapeInterval, params Address[] scrapeTargets)
|
||||
@ -19,7 +19,7 @@ namespace MetricsPlugin
|
||||
|
||||
public static IMetricsAccess WrapMetricsCollector(this CoreInterface ci, RunningPod metricsPod, IHasMetricsScrapeTarget scrapeTarget)
|
||||
{
|
||||
return ci.WrapMetricsCollector(metricsPod, scrapeTarget.MetricsScrapeTarget);
|
||||
return ci.WrapMetricsCollector(metricsPod, scrapeTarget.GetMetricsScrapeTarget());
|
||||
}
|
||||
|
||||
public static IMetricsAccess WrapMetricsCollector(this CoreInterface ci, RunningPod metricsPod, Address scrapeTarget)
|
||||
@ -29,12 +29,12 @@ namespace MetricsPlugin
|
||||
|
||||
public static IMetricsAccess[] GetMetricsFor(this CoreInterface ci, TimeSpan scrapeInterval, params IHasManyMetricScrapeTargets[] manyScrapeTargets)
|
||||
{
|
||||
return ci.GetMetricsFor(scrapeInterval, manyScrapeTargets.SelectMany(t => t.ScrapeTargets).ToArray());
|
||||
return ci.GetMetricsFor(scrapeInterval, manyScrapeTargets.SelectMany(t => t.GetMetricsScrapeTargets()).ToArray());
|
||||
}
|
||||
|
||||
public static IMetricsAccess[] GetMetricsFor(this CoreInterface ci, TimeSpan scrapeInterval, params IHasMetricsScrapeTarget[] scrapeTargets)
|
||||
{
|
||||
return ci.GetMetricsFor(scrapeInterval, scrapeTargets.Select(t => t.MetricsScrapeTarget).ToArray());
|
||||
return ci.GetMetricsFor(scrapeInterval, scrapeTargets.Select(t => t.GetMetricsScrapeTarget()).ToArray());
|
||||
}
|
||||
|
||||
public static IMetricsAccess[] GetMetricsFor(this CoreInterface ci, TimeSpan scrapeInterval, params Address[] scrapeTargets)
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
using Utils;
|
||||
|
||||
namespace MetricsPlugin
|
||||
{
|
||||
public interface IHasMetricsScrapeTarget
|
||||
{
|
||||
Address MetricsScrapeTarget { get; }
|
||||
}
|
||||
|
||||
public interface IHasManyMetricScrapeTargets
|
||||
{
|
||||
Address[] ScrapeTargets { get; }
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,4 @@
|
||||
using CodexPlugin;
|
||||
using Core;
|
||||
using DistTestCore;
|
||||
using CodexClient;
|
||||
using FileUtils;
|
||||
using Logging;
|
||||
using MetricsPlugin;
|
||||
@ -10,7 +8,6 @@ namespace ContinuousTests
|
||||
{
|
||||
public abstract class ContinuousTestLongTimeouts : ContinuousTest
|
||||
{
|
||||
public override ITimeSet TimeSet => new LongTimeSet();
|
||||
}
|
||||
|
||||
public abstract class ContinuousTest
|
||||
@ -45,13 +42,12 @@ namespace ContinuousTests
|
||||
public ILog Log { get; private set; } = null!;
|
||||
public IFileManager FileManager { get; private set; } = null!;
|
||||
public Configuration Configuration { get; private set; } = null!;
|
||||
public virtual ITimeSet TimeSet { get { return new DefaultTimeSet(); } }
|
||||
public CancellationToken CancelToken { get; private set; } = new CancellationToken();
|
||||
public NodeRunner NodeRunner { get; private set; } = null!;
|
||||
|
||||
public IMetricsAccess CreateMetricsAccess(IHasMetricsScrapeTarget target)
|
||||
{
|
||||
return CreateMetricsAccess(target.MetricsScrapeTarget);
|
||||
return CreateMetricsAccess(target.GetMetricsScrapeTarget());
|
||||
}
|
||||
|
||||
public IMetricsAccess CreateMetricsAccess(Address target)
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
using KubernetesWorkflow.Types;
|
||||
using Logging;
|
||||
using Utils;
|
||||
using WebUtils;
|
||||
|
||||
namespace ContinuousTests
|
||||
{
|
||||
|
||||
@ -3,8 +3,7 @@ using Logging;
|
||||
using Utils;
|
||||
using Core;
|
||||
using CodexPlugin;
|
||||
using KubernetesWorkflow.Types;
|
||||
using KubernetesWorkflow;
|
||||
using CodexClient;
|
||||
|
||||
namespace ContinuousTests
|
||||
{
|
||||
|
||||
@ -7,6 +7,7 @@ using DistTestCore.Logs;
|
||||
using Core;
|
||||
using KubernetesWorkflow.Types;
|
||||
using TaskFactory = Utils.TaskFactory;
|
||||
using CodexClient;
|
||||
|
||||
namespace ContinuousTests
|
||||
{
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
using CodexPlugin;
|
||||
using CodexClient;
|
||||
using Core;
|
||||
using DistTestCore.Logs;
|
||||
using Logging;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ContinuousTests
|
||||
{
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using CodexPlugin;
|
||||
using CodexClient;
|
||||
using FileUtils;
|
||||
using NUnit.Framework;
|
||||
using Utils;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using CodexPlugin;
|
||||
using CodexClient;
|
||||
using CodexTests.Helpers;
|
||||
using ContinuousTests;
|
||||
using NUnit.Framework;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using CodexPlugin;
|
||||
using CodexClient;
|
||||
using FileUtils;
|
||||
using Logging;
|
||||
using NUnit.Framework;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using CodexPlugin;
|
||||
using CodexClient;
|
||||
using CodexTests;
|
||||
using DistTestCore;
|
||||
using FileUtils;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using CodexPlugin;
|
||||
using CodexClient;
|
||||
using CodexTests;
|
||||
using DistTestCore;
|
||||
using FileUtils;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using CodexPlugin;
|
||||
using CodexClient;
|
||||
using DistTestCore;
|
||||
using NUnit.Framework;
|
||||
using Utils;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using CodexPlugin;
|
||||
using CodexClient;
|
||||
using DistTestCore;
|
||||
using FileUtils;
|
||||
using NUnit.Framework;
|
||||
@ -38,11 +38,11 @@ public class ScalabilityTests : CodexDistTest
|
||||
var testFile = GenerateTestFile(fileSizeInMb.MB());
|
||||
|
||||
LogNodeStatus(uploader);
|
||||
var contentId = uploader.UploadFile(testFile, f => LogNodeStatus(uploader));
|
||||
var contentId = uploader.UploadFile(testFile);
|
||||
LogNodeStatus(uploader);
|
||||
|
||||
LogNodeStatus(downloader);
|
||||
var downloadedFile = downloader.DownloadContent(contentId, f => LogNodeStatus(downloader));
|
||||
var downloadedFile = downloader.DownloadContent(contentId);
|
||||
LogNodeStatus(downloader);
|
||||
|
||||
downloadedFile!.AssertIsEqual(testFile);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using CodexPlugin;
|
||||
using CodexClient;
|
||||
using CodexTests;
|
||||
using FileUtils;
|
||||
using NUnit.Framework;
|
||||
|
||||
@ -1,11 +1,6 @@
|
||||
using CodexPlugin;
|
||||
using CodexClient;
|
||||
using CodexTests;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Utils;
|
||||
|
||||
namespace CodexReleaseTests.DataTests
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using CodexPlugin;
|
||||
using CodexClient;
|
||||
using CodexPlugin;
|
||||
using CodexTests;
|
||||
using FileUtils;
|
||||
using NUnit.Framework;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using CodexPlugin;
|
||||
using CodexClient;
|
||||
using CodexPlugin;
|
||||
using CodexTests;
|
||||
using NUnit.Framework;
|
||||
using Utils;
|
||||
|
||||
@ -1,11 +1,6 @@
|
||||
using CodexPlugin;
|
||||
using CodexClient;
|
||||
using CodexTests;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodexReleaseTests.DataTests
|
||||
{
|
||||
|
||||
@ -1,13 +1,6 @@
|
||||
using CodexContractsPlugin;
|
||||
using CodexClient;
|
||||
using CodexContractsPlugin.Marketplace;
|
||||
using CodexPlugin;
|
||||
using CodexTests;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Utils;
|
||||
|
||||
namespace CodexReleaseTests.MarketTests
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
using CodexClient;
|
||||
using CodexContractsPlugin;
|
||||
using CodexPlugin;
|
||||
using GethPlugin;
|
||||
using NUnit.Framework;
|
||||
using Utils;
|
||||
|
||||
@ -32,7 +29,7 @@ namespace CodexReleaseTests.MarketTests
|
||||
request.WaitForStorageContractStarted();
|
||||
AssertContractSlotsAreFilledByHosts(request, hosts);
|
||||
|
||||
request.WaitForStorageContractFinished(GetContracts());
|
||||
request.WaitForStorageContractFinished();
|
||||
|
||||
AssertClientHasPaidForContract(pricePerSlotPerSecond, client, request, hosts);
|
||||
AssertHostsWerePaidForContract(pricePerSlotPerSecond, request, hosts);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using CodexContractsPlugin;
|
||||
using CodexClient;
|
||||
using CodexContractsPlugin;
|
||||
using CodexContractsPlugin.Marketplace;
|
||||
using CodexPlugin;
|
||||
using CodexTests;
|
||||
@ -67,7 +68,7 @@ namespace CodexReleaseTests.MarketTests
|
||||
Assert.That(GetTstBalance(host).TstWei, Is.EqualTo(StartingBalanceTST.Tst().TstWei));
|
||||
Assert.That(GetEthBalance(host).Wei, Is.EqualTo(StartingBalanceEth.Eth().Wei));
|
||||
|
||||
host.Marketplace.MakeStorageAvailable(new CodexPlugin.StorageAvailability(
|
||||
host.Marketplace.MakeStorageAvailable(new StorageAvailability(
|
||||
totalSpace: HostAvailabilitySize,
|
||||
maxDuration: HostAvailabilityMaxDuration,
|
||||
minPriceForTotalSpace: 1.TstWei(),
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
using CodexClient;
|
||||
using CodexContractsPlugin;
|
||||
using CodexPlugin;
|
||||
using GethPlugin;
|
||||
using NUnit.Framework;
|
||||
using Utils;
|
||||
|
||||
@ -36,7 +33,7 @@ namespace CodexReleaseTests.MarketTests
|
||||
All(requests, r => r.WaitForStorageContractStarted());
|
||||
All(requests, r => AssertContractSlotsAreFilledByHosts(r, hosts));
|
||||
|
||||
All(requests, r => r.WaitForStorageContractFinished(GetContracts()));
|
||||
All(requests, r => r.WaitForStorageContractFinished());
|
||||
|
||||
// todo: removed from codexclient:
|
||||
//contracts.WaitUntilNextPeriod();
|
||||
|
||||
@ -1,13 +1,7 @@
|
||||
using CodexPlugin;
|
||||
using CodexClient;
|
||||
using CodexTests;
|
||||
using CodexTests.Helpers;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Utils;
|
||||
|
||||
namespace CodexReleaseTests.NodeTests
|
||||
{
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using CodexPlugin;
|
||||
using CodexClient;
|
||||
using CodexPlugin;
|
||||
using DistTestCore;
|
||||
using MetricsPlugin;
|
||||
using NUnit.Framework;
|
||||
|
||||
@ -108,7 +108,7 @@ namespace CodexTests.BasicTests
|
||||
AssertStorageRequest(request, purchase, contracts, client);
|
||||
AssertContractSlot(contracts, request, 0);
|
||||
|
||||
purchaseContract.WaitForStorageContractFinished(contracts);
|
||||
purchaseContract.WaitForStorageContractFinished();
|
||||
|
||||
// todo: removed from codexclient:
|
||||
//contracts.WaitUntilNextPeriod();
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using CodexContractsPlugin;
|
||||
using CodexClient;
|
||||
using NUnit.Framework;
|
||||
using Utils;
|
||||
|
||||
namespace CodexTests.PeerDiscoveryTests
|
||||
{
|
||||
|
||||
@ -20,7 +20,7 @@ namespace CodexTests.UtilityTests
|
||||
private readonly RewardRepo repo = new RewardRepo();
|
||||
private readonly TestToken hostInitialBalance = 3000000.TstWei();
|
||||
private readonly TestToken clientInitialBalance = 1000000000.TstWei();
|
||||
private readonly EthAccount clientAccount = EthAccount.GenerateNew();
|
||||
private readonly EthAccount clientAccount = EthAccountGenerator.GenerateNew();
|
||||
private readonly List<EthAccount> hostAccounts = new List<EthAccount>();
|
||||
private readonly List<ulong> rewardsSeen = new List<ulong>();
|
||||
private readonly TimeSpan rewarderInterval = TimeSpan.FromMinutes(1);
|
||||
@ -46,7 +46,7 @@ namespace CodexTests.UtilityTests
|
||||
|
||||
var purchaseContract = ClientPurchasesStorage(client);
|
||||
purchaseContract.WaitForStorageContractStarted();
|
||||
purchaseContract.WaitForStorageContractFinished(contracts);
|
||||
purchaseContract.WaitForStorageContractFinished();
|
||||
|
||||
// todo: removed from codexclient:
|
||||
//contracts.WaitUntilNextPeriod();
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using AutoClient.Modes.FolderStore;
|
||||
using CodexClient;
|
||||
using Logging;
|
||||
|
||||
namespace AutoClient
|
||||
@ -15,7 +16,6 @@ namespace AutoClient
|
||||
);
|
||||
|
||||
Generator = CreateGenerator();
|
||||
CidRepo = new CidRepo(config);
|
||||
Performance = new Performance(new LogSplitter(
|
||||
new FileLog(Path.Combine(config.LogPath, "performance")),
|
||||
new ConsoleLog()
|
||||
@ -29,15 +29,17 @@ namespace AutoClient
|
||||
{
|
||||
FolderWorkDispatcher = null!;
|
||||
}
|
||||
|
||||
CodexNodeFactory = new CodexNodeFactory(log: Log, dataDir: Config.DataPath);
|
||||
}
|
||||
|
||||
public Configuration Config { get; }
|
||||
public ILog Log { get; }
|
||||
public IFileGenerator Generator { get; }
|
||||
public CancellationTokenSource Cts { get; } = new CancellationTokenSource();
|
||||
public CidRepo CidRepo { get; }
|
||||
public Performance Performance { get; }
|
||||
public FolderWorkDispatcher FolderWorkDispatcher { get; }
|
||||
public CodexNodeFactory CodexNodeFactory { get; }
|
||||
|
||||
private IFileGenerator CreateGenerator()
|
||||
{
|
||||
|
||||
@ -1,24 +1,19 @@
|
||||
using CodexOpenApi;
|
||||
using CodexPlugin;
|
||||
using Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Utils;
|
||||
using Logging;
|
||||
|
||||
namespace AutoClient
|
||||
{
|
||||
public class AutomaticPurchaser
|
||||
{
|
||||
private readonly App app;
|
||||
private readonly ILog log;
|
||||
private readonly ICodexInstance instance;
|
||||
private readonly CodexNode codex;
|
||||
private readonly CodexWrapper node;
|
||||
private Task workerTask = Task.CompletedTask;
|
||||
private App app => instance.App;
|
||||
|
||||
public AutomaticPurchaser(ILog log, ICodexInstance instance, CodexNode codex)
|
||||
public AutomaticPurchaser(App app, ILog log, CodexWrapper node)
|
||||
{
|
||||
this.app = app;
|
||||
this.log = log;
|
||||
this.instance = instance;
|
||||
this.codex = codex;
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
@ -40,7 +35,6 @@ namespace AutoClient
|
||||
{
|
||||
var pid = await StartNewPurchase();
|
||||
await WaitTillFinished(pid);
|
||||
await DownloadForeignCid();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -50,27 +44,13 @@ namespace AutoClient
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DownloadForeignCid()
|
||||
{
|
||||
var cid = app.CidRepo.GetForeignCid(instance.NodeId);
|
||||
if (cid == null) return;
|
||||
|
||||
var size = app.CidRepo.GetSizeForCid(cid);
|
||||
if (size == null) return;
|
||||
|
||||
var filename = Guid.NewGuid().ToString().ToLowerInvariant();
|
||||
await codex.DownloadCid(filename, cid, size);
|
||||
|
||||
DeleteFile(filename);
|
||||
}
|
||||
|
||||
private async Task<string> StartNewPurchase()
|
||||
{
|
||||
var file = await CreateFile();
|
||||
try
|
||||
{
|
||||
var cid = await codex.UploadFile(file);
|
||||
var response = await codex.RequestStorage(cid);
|
||||
var cid = node.UploadFile(file);
|
||||
var response = node.RequestStorage(cid);
|
||||
return response.PurchaseId;
|
||||
}
|
||||
finally
|
||||
@ -92,7 +72,7 @@ namespace AutoClient
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
app.Log.Error($"Failed to delete file '{file}': {exc}");
|
||||
log.Error($"Failed to delete file '{file}': {exc}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,7 +83,7 @@ namespace AutoClient
|
||||
var emptyResponseTolerance = 10;
|
||||
while (!app.Cts.Token.IsCancellationRequested)
|
||||
{
|
||||
var purchase = await codex.GetStoragePurchase(pid);
|
||||
var purchase = node.GetStoragePurchase(pid);
|
||||
if (purchase == null)
|
||||
{
|
||||
await FixedShortDelay();
|
||||
|
||||
@ -1,86 +0,0 @@
|
||||
namespace AutoClient
|
||||
{
|
||||
public class CidRepo
|
||||
{
|
||||
private readonly Random random = new Random();
|
||||
private readonly object _lock = new object();
|
||||
private readonly List<CidEntry> entries = new List<CidEntry>();
|
||||
private readonly Configuration config;
|
||||
|
||||
public CidRepo(Configuration config)
|
||||
{
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public void Add(string nodeId, string cid, long knownSize)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
entries.Add(new CidEntry(nodeId, cid, knownSize));
|
||||
if (entries.Count > 1000) entries.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddEncoded(string originalCid, string encodedCid)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var entry = entries.SingleOrDefault(e => e.Cid == originalCid);
|
||||
if (entry == null) return;
|
||||
|
||||
entry.Encoded = encodedCid;
|
||||
}
|
||||
}
|
||||
|
||||
public string? GetForeignCid(string myNodeId)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (!entries.Any()) return null;
|
||||
var available = entries.Where(e => e.NodeId != myNodeId).ToArray();
|
||||
if (!available.Any()) return null;
|
||||
|
||||
var i = random.Next(0, available.Length);
|
||||
var entry = available[i];
|
||||
|
||||
if (entry.CreatedUtc < (DateTime.UtcNow + TimeSpan.FromMinutes(config.ContractDurationMinutes)))
|
||||
{
|
||||
entries.Remove(entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
return entry.Cid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long? GetSizeForCid(string cid)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var entry = entries.SingleOrDefault(e => e.Cid == cid);
|
||||
if (entry == null) return null;
|
||||
return entry.KnownSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CidEntry
|
||||
{
|
||||
public CidEntry(string nodeId, string cid, long knownSize)
|
||||
{
|
||||
NodeId = nodeId;
|
||||
Cid = cid;
|
||||
KnownSize = knownSize;
|
||||
}
|
||||
|
||||
public string NodeId { get; }
|
||||
public string Cid { get; }
|
||||
public string Encoded { get; set; } = string.Empty;
|
||||
public long KnownSize { get; }
|
||||
public DateTime CreatedUtc { get; } = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
32
Tools/AutoClient/CodexContext.cs
Normal file
32
Tools/AutoClient/CodexContext.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using CodexClient;
|
||||
using Utils;
|
||||
|
||||
namespace AutoClient
|
||||
{
|
||||
public interface ICodexContext
|
||||
{
|
||||
string NodeId { get; }
|
||||
App App { get; }
|
||||
ICodexNode Codex { get; }
|
||||
HttpClient Client { get; }
|
||||
Address Address { get; }
|
||||
}
|
||||
|
||||
public class CodexContext : ICodexContext
|
||||
{
|
||||
public CodexContext(App app, ICodexNode codex, HttpClient client, Address address)
|
||||
{
|
||||
App = app;
|
||||
Codex = codex;
|
||||
Client = client;
|
||||
Address = address;
|
||||
NodeId = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
public string NodeId { get; }
|
||||
public App App { get; }
|
||||
public ICodexNode Codex { get; }
|
||||
public HttpClient Client { get; }
|
||||
public Address Address { get; }
|
||||
}
|
||||
}
|
||||
@ -1,162 +0,0 @@
|
||||
using CodexClient;
|
||||
using CodexOpenApi;
|
||||
using CodexPlugin;
|
||||
using Logging;
|
||||
using Nethereum.Model;
|
||||
using Newtonsoft.Json;
|
||||
using Utils;
|
||||
|
||||
namespace AutoClient
|
||||
{
|
||||
public interface ICodexInstance
|
||||
{
|
||||
string NodeId { get; }
|
||||
App App { get; }
|
||||
CodexApi Codex { get; }
|
||||
HttpClient Client { get; }
|
||||
Address Address { get; }
|
||||
}
|
||||
|
||||
public class CodexInstance : ICodexInstance
|
||||
{
|
||||
public CodexInstance(App app, CodexApi codex, HttpClient client, Address address)
|
||||
{
|
||||
App = app;
|
||||
Codex = codex;
|
||||
Client = client;
|
||||
Address = address;
|
||||
NodeId = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
public string NodeId { get; }
|
||||
public App App { get; }
|
||||
public CodexApi Codex { get; }
|
||||
public HttpClient Client { get; }
|
||||
public Address Address { get; }
|
||||
}
|
||||
|
||||
public class CodexNode
|
||||
{
|
||||
private readonly App app;
|
||||
private readonly ICodexInstance codex;
|
||||
|
||||
public CodexNode(App app, ICodexInstance instance)
|
||||
{
|
||||
this.app = app;
|
||||
codex = instance;
|
||||
}
|
||||
|
||||
public async Task DownloadCid(string filename, string cid, long? size)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
using var fileStream = File.OpenWrite(filename);
|
||||
var fileResponse = await codex.Codex.DownloadNetworkStreamAsync(cid);
|
||||
fileResponse.Stream.CopyTo(fileStream);
|
||||
var time = sw.Elapsed;
|
||||
app.Performance.DownloadSuccessful(size, time);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
app.Performance.DownloadFailed(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ContentId> UploadFile(string filename)
|
||||
{
|
||||
using var fileStream = File.OpenRead(filename);
|
||||
try
|
||||
{
|
||||
var info = new FileInfo(filename);
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
var cid = await UploadStream(fileStream, filename);
|
||||
var time = sw.Elapsed;
|
||||
app.Performance.UploadSuccessful(info.Length, time);
|
||||
app.CidRepo.Add(codex.NodeId, cid.Id, info.Length);
|
||||
return cid;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
app.Performance.UploadFailed(exc);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<RequestStorageResult> RequestStorage(ContentId cid)
|
||||
{
|
||||
app.Log.Debug("Requesting storage for " + cid.Id);
|
||||
var result = await codex.Codex.CreateStorageRequestAsync(cid.Id, new StorageRequestCreation()
|
||||
{
|
||||
Collateral = app.Config.RequiredCollateral.ToString(),
|
||||
Duration = (app.Config.ContractDurationMinutes * 60).ToString(),
|
||||
Expiry = (app.Config.ContractExpiryMinutes * 60).ToString(),
|
||||
Nodes = app.Config.NumHosts,
|
||||
Reward = app.Config.Price.ToString(),
|
||||
ProofProbability = "15",
|
||||
Tolerance = app.Config.HostTolerance
|
||||
}, app.Cts.Token);
|
||||
|
||||
app.Log.Debug("Purchase ID: " + result);
|
||||
|
||||
var encoded = await GetEncodedCid(result);
|
||||
app.CidRepo.AddEncoded(cid.Id, encoded);
|
||||
|
||||
return new RequestStorageResult(result, new ContentId(encoded));
|
||||
}
|
||||
|
||||
public class RequestStorageResult
|
||||
{
|
||||
public RequestStorageResult(string purchaseId, ContentId encodedCid)
|
||||
{
|
||||
PurchaseId = purchaseId;
|
||||
EncodedCid = encodedCid;
|
||||
}
|
||||
|
||||
public string PurchaseId { get; }
|
||||
public ContentId EncodedCid { get; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{PurchaseId} (cid: {EncodedCid})";
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<StoragePurchase?> GetStoragePurchase(string pid)
|
||||
{
|
||||
// openapi still don't match code.
|
||||
var str = await codex.Client.GetStringAsync($"{codex.Address.Host}:{codex.Address.Port}/api/codex/v1/storage/purchases/{pid}");
|
||||
if (string.IsNullOrEmpty(str)) return null;
|
||||
return JsonConvert.DeserializeObject<StoragePurchase>(str);
|
||||
}
|
||||
|
||||
private async Task<ContentId> UploadStream(FileStream fileStream, string filename)
|
||||
{
|
||||
app.Log.Debug($"Uploading file...");
|
||||
var response = await codex.Codex.UploadAsync(
|
||||
content_type: "application/octet-stream",
|
||||
content_disposition: $"attachment; filename=\"{filename}\"",
|
||||
fileStream, app.Cts.Token);
|
||||
|
||||
if (string.IsNullOrEmpty(response)) FrameworkAssert.Fail("Received empty response.");
|
||||
if (response.StartsWith("Unable to store block")) FrameworkAssert.Fail("Node failed to store block.");
|
||||
|
||||
app.Log.Debug($"Uploaded file. Received contentId: '{response}'.");
|
||||
return new ContentId(response);
|
||||
}
|
||||
|
||||
private async Task<string> GetEncodedCid(string pid)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sp = (await GetStoragePurchase(pid))!;
|
||||
return sp.Request.Content.Cid;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
app.Log.Error(ex.ToString());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
Tools/AutoClient/CodexWrapper.cs
Normal file
45
Tools/AutoClient/CodexWrapper.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using CodexClient;
|
||||
using FileUtils;
|
||||
using Utils;
|
||||
|
||||
namespace AutoClient
|
||||
{
|
||||
public class CodexWrapper
|
||||
{
|
||||
private readonly App app;
|
||||
|
||||
public CodexWrapper(App app, ICodexNode node)
|
||||
{
|
||||
this.app = app;
|
||||
Node = node;
|
||||
}
|
||||
|
||||
public ICodexNode Node { get; }
|
||||
|
||||
public ContentId UploadFile(string filepath)
|
||||
{
|
||||
return Node.UploadFile(TrackedFile.FromPath(app.Log, filepath));
|
||||
}
|
||||
|
||||
public IStoragePurchaseContract RequestStorage(ContentId cid)
|
||||
{
|
||||
app.Log.Debug("Requesting storage for " + cid.Id);
|
||||
var result = Node.Marketplace.RequestStorage(new StoragePurchaseRequest(cid)
|
||||
{
|
||||
RequiredCollateral = app.Config.RequiredCollateral.Tst(),
|
||||
Duration = TimeSpan.FromMinutes(app.Config.ContractDurationMinutes),
|
||||
Expiry = TimeSpan.FromMinutes(app.Config.ContractExpiryMinutes),
|
||||
MinRequiredNumberOfNodes = Convert.ToUInt32(app.Config.NumHosts),
|
||||
NodeFailureTolerance = Convert.ToUInt32(app.Config.HostTolerance),
|
||||
PricePerSlotPerSecond = app.Config.Price.Tst(),
|
||||
ProofProbability = 15
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public StoragePurchase? GetStoragePurchase(string pid)
|
||||
{
|
||||
return Node.GetPurchaseStatus(pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,30 +1,29 @@
|
||||
using Logging;
|
||||
using CodexClient;
|
||||
using Logging;
|
||||
|
||||
namespace AutoClient.Modes.FolderStore
|
||||
{
|
||||
public class FileWorker : FileStatus
|
||||
{
|
||||
private readonly App app;
|
||||
private readonly CodexWrapper node;
|
||||
private readonly ILog log;
|
||||
private readonly ICodexInstance instance;
|
||||
private readonly PurchaseInfo purchaseInfo;
|
||||
private readonly string sourceFilename;
|
||||
private readonly Action onFileUploaded;
|
||||
private readonly Action onNewPurchase;
|
||||
private readonly CodexNode codex;
|
||||
|
||||
public FileWorker(App app, ICodexInstance instance, PurchaseInfo purchaseInfo, string folder, FileIndex fileIndex, Action onFileUploaded, Action onNewPurchase)
|
||||
public FileWorker(App app, CodexWrapper node, PurchaseInfo purchaseInfo, string folder, FileIndex fileIndex, Action onFileUploaded, Action onNewPurchase)
|
||||
: base(app, folder, fileIndex.File + ".json", purchaseInfo)
|
||||
{
|
||||
this.app = app;
|
||||
this.node = node;
|
||||
log = new LogPrefixer(app.Log, GetFileTag(fileIndex));
|
||||
this.instance = instance;
|
||||
this.purchaseInfo = purchaseInfo;
|
||||
sourceFilename = fileIndex.File;
|
||||
if (sourceFilename.ToLowerInvariant().EndsWith(".json")) throw new Exception("Not an era file.");
|
||||
this.onFileUploaded = onFileUploaded;
|
||||
this.onNewPurchase = onNewPurchase;
|
||||
codex = new CodexNode(app, instance);
|
||||
}
|
||||
|
||||
public int FailureCounter => State.FailureCounter;
|
||||
@ -34,14 +33,14 @@ namespace AutoClient.Modes.FolderStore
|
||||
newState.LastUpdate = DateTime.MinValue;
|
||||
}
|
||||
|
||||
public async Task Update()
|
||||
public void Update()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsCurrentlyRunning() && UpdatedRecently()) return;
|
||||
|
||||
Log($"Updating for '{sourceFilename}'...");
|
||||
await EnsureRecentPurchase();
|
||||
EnsureRecentPurchase();
|
||||
SaveState();
|
||||
app.Log.Log("");
|
||||
}
|
||||
@ -60,12 +59,12 @@ namespace AutoClient.Modes.FolderStore
|
||||
return State.LastUpdate + TimeSpan.FromMinutes(15) > now;
|
||||
}
|
||||
|
||||
private async Task<string> EnsureCid()
|
||||
private string EnsureCid()
|
||||
{
|
||||
Log($"Checking CID...");
|
||||
|
||||
if (!string.IsNullOrEmpty(State.EncodedCid) &&
|
||||
await DoesCidExistInNetwork(State.EncodedCid))
|
||||
DoesCidExistInNetwork(State.EncodedCid))
|
||||
{
|
||||
Log("Encoded-CID successfully found in the network.");
|
||||
// TODO: Using the encoded CID currently would result in double-encoding of the dataset.
|
||||
@ -75,7 +74,7 @@ namespace AutoClient.Modes.FolderStore
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(State.Cid) &&
|
||||
await DoesCidExistInNetwork(State.Cid))
|
||||
DoesCidExistInNetwork(State.Cid))
|
||||
{
|
||||
Log("Basic-CID successfully found in the network.");
|
||||
return State.Cid;
|
||||
@ -87,7 +86,7 @@ namespace AutoClient.Modes.FolderStore
|
||||
}
|
||||
|
||||
Log($"Uploading...");
|
||||
var cid = await codex.UploadFile(sourceFilename);
|
||||
var cid = node.UploadFile(sourceFilename);
|
||||
onFileUploaded();
|
||||
Log("Got Basic-CID: " + cid);
|
||||
State.Cid = cid.Id;
|
||||
@ -95,7 +94,7 @@ namespace AutoClient.Modes.FolderStore
|
||||
return State.Cid;
|
||||
}
|
||||
|
||||
private async Task<bool> DoesCidExistInNetwork(string cid)
|
||||
private bool DoesCidExistInNetwork(string cid)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -107,7 +106,7 @@ namespace AutoClient.Modes.FolderStore
|
||||
cts.Cancel();
|
||||
});
|
||||
|
||||
var manifest = await instance.Codex.DownloadNetworkManifestAsync(cid, cts.Token);
|
||||
var manifest = node.Node.DownloadManifestOnly(new ContentId(cid));
|
||||
if (manifest == null) return false;
|
||||
}
|
||||
catch
|
||||
@ -117,23 +116,23 @@ namespace AutoClient.Modes.FolderStore
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task EnsureRecentPurchase()
|
||||
private void EnsureRecentPurchase()
|
||||
{
|
||||
Log($"Checking recent purchase...");
|
||||
var recent = GetMostRecent();
|
||||
if (recent == null)
|
||||
{
|
||||
Log($"No recent purchase.");
|
||||
await MakeNewPurchase();
|
||||
MakeNewPurchase();
|
||||
return;
|
||||
}
|
||||
|
||||
await UpdatePurchase(recent);
|
||||
UpdatePurchase(recent);
|
||||
|
||||
if (recent.Expiry.HasValue)
|
||||
{
|
||||
Log($"Purchase has failed or expired.");
|
||||
await MakeNewPurchase();
|
||||
MakeNewPurchase();
|
||||
State.FailureCounter++;
|
||||
return;
|
||||
}
|
||||
@ -141,7 +140,7 @@ namespace AutoClient.Modes.FolderStore
|
||||
if (recent.Finish.HasValue)
|
||||
{
|
||||
Log($"Purchase has finished.");
|
||||
await MakeNewPurchase();
|
||||
MakeNewPurchase();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -149,7 +148,7 @@ namespace AutoClient.Modes.FolderStore
|
||||
if (recent.Started.HasValue && DateTime.UtcNow > safeEnd)
|
||||
{
|
||||
Log($"Purchase is going to expire soon.");
|
||||
await MakeNewPurchase();
|
||||
MakeNewPurchase();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -168,12 +167,12 @@ namespace AutoClient.Modes.FolderStore
|
||||
Log($"Purchase is running.");
|
||||
}
|
||||
|
||||
private async Task UpdatePurchase(WorkerPurchase recent)
|
||||
private void UpdatePurchase(WorkerPurchase recent)
|
||||
{
|
||||
if (string.IsNullOrEmpty(recent.Pid)) throw new Exception("No purchaseID!");
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var purchase = await codex.GetStoragePurchase(recent.Pid);
|
||||
var purchase = node.GetStoragePurchase(recent.Pid);
|
||||
if (purchase == null)
|
||||
{
|
||||
Log($"No purchase information found for PID '{recent.Pid}'. Consider this one expired.");
|
||||
@ -210,15 +209,15 @@ namespace AutoClient.Modes.FolderStore
|
||||
SaveState();
|
||||
}
|
||||
|
||||
private async Task MakeNewPurchase()
|
||||
private void MakeNewPurchase()
|
||||
{
|
||||
var cid = await EnsureCid();
|
||||
var cid = EnsureCid();
|
||||
if (string.IsNullOrEmpty(cid)) throw new Exception("No cid!");
|
||||
|
||||
Log($"Creating new purchase...");
|
||||
var response = await codex.RequestStorage(new CodexPlugin.ContentId(cid));
|
||||
var response = node.RequestStorage(new ContentId(cid));
|
||||
var purchaseId = response.PurchaseId;
|
||||
var encodedCid = response.EncodedCid;
|
||||
var encodedCid = response.ContentId;
|
||||
if (string.IsNullOrEmpty(purchaseId) ||
|
||||
purchaseId == "Unable to encode manifest" ||
|
||||
purchaseId == "Purchasing not available" ||
|
||||
@ -248,7 +247,7 @@ namespace AutoClient.Modes.FolderStore
|
||||
while (DateTime.UtcNow < timeout)
|
||||
{
|
||||
Thread.Sleep(5000);
|
||||
await UpdatePurchase(newPurchase);
|
||||
UpdatePurchase(newPurchase);
|
||||
if (newPurchase.Submitted.HasValue)
|
||||
{
|
||||
Log("New purchase successfully submitted.");
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using CodexOpenApi;
|
||||
using System.IO.Compression;
|
||||
using System.IO.Compression;
|
||||
using static AutoClient.Modes.FolderStore.FolderWorkOverview;
|
||||
|
||||
namespace AutoClient.Modes.FolderStore
|
||||
@ -22,7 +21,7 @@ namespace AutoClient.Modes.FolderStore
|
||||
newState.LastOverviewUpdate = DateTime.MinValue;
|
||||
}
|
||||
|
||||
public async Task Update(ICodexInstance instance)
|
||||
public void Update(CodexWrapper instance)
|
||||
{
|
||||
var jsonFiles = Directory.GetFiles(Folder).Where(f => f.ToLowerInvariant().EndsWith(".json") && !f.Contains(OverviewFilename)).ToList();
|
||||
|
||||
@ -54,7 +53,7 @@ namespace AutoClient.Modes.FolderStore
|
||||
State.UncommitedChanges = 0;
|
||||
SaveState();
|
||||
|
||||
await CreateNewOverviewZip(jsonFiles, FilePath, instance);
|
||||
CreateNewOverviewZip(jsonFiles, FilePath, instance);
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,7 +63,7 @@ namespace AutoClient.Modes.FolderStore
|
||||
SaveState();
|
||||
}
|
||||
|
||||
private async Task CreateNewOverviewZip(List<string> jsonFiles, string filePath, ICodexInstance instance)
|
||||
private void CreateNewOverviewZip(List<string> jsonFiles, string filePath, CodexWrapper node)
|
||||
{
|
||||
Log("");
|
||||
Log("");
|
||||
@ -74,15 +73,14 @@ namespace AutoClient.Modes.FolderStore
|
||||
Log("Uploading to Codex...");
|
||||
try
|
||||
{
|
||||
var codex = new CodexNode(app, instance);
|
||||
var cid = await codex.UploadFile(zipFilename);
|
||||
var cid = node.UploadFile(zipFilename);
|
||||
Log($"Upload successful: New overview zipfile CID = '{cid.Id}'");
|
||||
Log("Requesting storage for it...");
|
||||
var result = await codex.RequestStorage(cid);
|
||||
Log("Storage requested. Purchase ID: " + result);
|
||||
var result = node.RequestStorage(cid);
|
||||
Log("Storage requested. Purchase ID: " + result.PurchaseId);
|
||||
|
||||
var outFile = Path.Combine(app.Config.DataPath, "OverviewZip.cid");
|
||||
File.AppendAllLines(outFile, [DateTime.UtcNow.ToString("o") + " - " + result.EncodedCid.Id]);
|
||||
File.AppendAllLines(outFile, [DateTime.UtcNow.ToString("o") + " - " + result.ContentId.Id]);
|
||||
Log($">>> [{outFile}] has been updated. <<<");
|
||||
}
|
||||
catch (Exception exc)
|
||||
|
||||
@ -17,13 +17,13 @@ namespace AutoClient.Modes
|
||||
this.purchaseInfo = purchaseInfo;
|
||||
}
|
||||
|
||||
public void Start(ICodexInstance instance, int index)
|
||||
public void Start(CodexWrapper instance, int index)
|
||||
{
|
||||
checkTask = Task.Run(async () =>
|
||||
checkTask = Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await RunChecker(instance);
|
||||
RunChecker(instance);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -33,14 +33,14 @@ namespace AutoClient.Modes
|
||||
});
|
||||
}
|
||||
|
||||
private async Task RunChecker(ICodexInstance instance)
|
||||
private void RunChecker(CodexWrapper instance)
|
||||
{
|
||||
var i = 0;
|
||||
while (!cts.IsCancellationRequested)
|
||||
{
|
||||
Thread.Sleep(2000);
|
||||
|
||||
var worker = await ProcessWorkItem(instance);
|
||||
var worker = ProcessWorkItem(instance);
|
||||
if (worker.FailureCounter > 5)
|
||||
{
|
||||
throw new Exception("Worker has failure count > 5. Stopping AutoClient...");
|
||||
@ -51,16 +51,16 @@ namespace AutoClient.Modes
|
||||
{
|
||||
i = 0;
|
||||
var overview = new FolderWorkOverview(app, purchaseInfo, folder);
|
||||
await overview.Update(instance);
|
||||
overview.Update(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<FileWorker> ProcessWorkItem(ICodexInstance instance)
|
||||
private FileWorker ProcessWorkItem(CodexWrapper instance)
|
||||
{
|
||||
var file = app.FolderWorkDispatcher.GetFileToCheck();
|
||||
var worker = new FileWorker(app, instance, purchaseInfo, folder, file, OnFileUploaded, OnNewPurchase);
|
||||
await worker.Update();
|
||||
worker.Update();
|
||||
if (worker.IsBusy()) app.FolderWorkDispatcher.WorkerIsBusy();
|
||||
return worker;
|
||||
}
|
||||
|
||||
@ -1,14 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AutoClient.Modes
|
||||
namespace AutoClient.Modes
|
||||
{
|
||||
public interface IMode
|
||||
{
|
||||
void Start(ICodexInstance instance, int index);
|
||||
void Start(CodexWrapper node, int index);
|
||||
void Stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,11 +13,11 @@ namespace AutoClient.Modes
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
public void Start(ICodexInstance instance, int index)
|
||||
public void Start(CodexWrapper node, int index)
|
||||
{
|
||||
for (var i = 0; i < app.Config.NumConcurrentPurchases; i++)
|
||||
{
|
||||
purchasers.Add(new AutomaticPurchaser(new LogPrefixer(app.Log, $"({i}) "), instance, new CodexNode(app, instance)));
|
||||
purchasers.Add(new AutomaticPurchaser(app, new LogPrefixer(app.Log, $"({i}) "), node));
|
||||
}
|
||||
|
||||
var delayPerPurchaser =
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
using AutoClient;
|
||||
using AutoClient.Modes;
|
||||
using AutoClient.Modes.FolderStore;
|
||||
using CodexOpenApi;
|
||||
using CodexClient;
|
||||
using Utils;
|
||||
|
||||
public class Program
|
||||
@ -34,10 +34,11 @@ public class Program
|
||||
|
||||
public async Task Run()
|
||||
{
|
||||
var codexInstances = await CreateCodexInstances();
|
||||
await Task.CompletedTask;
|
||||
var codexNodes = CreateCodexWrappers();
|
||||
|
||||
var i = 0;
|
||||
foreach (var cdx in codexInstances)
|
||||
foreach (var cdx in codexNodes)
|
||||
{
|
||||
var mode = CreateMode();
|
||||
modes.Add(mode);
|
||||
@ -74,20 +75,20 @@ public class Program
|
||||
));
|
||||
}
|
||||
|
||||
private async Task<CodexInstance[]> CreateCodexInstances()
|
||||
private CodexWrapper[] CreateCodexWrappers()
|
||||
{
|
||||
var endpointStrs = app.Config.CodexEndpoints.Split(";", StringSplitOptions.RemoveEmptyEntries);
|
||||
var result = new List<CodexInstance>();
|
||||
var result = new List<CodexWrapper>();
|
||||
|
||||
foreach (var e in endpointStrs)
|
||||
{
|
||||
result.Add(await CreateCodexInstance(e));
|
||||
result.Add(CreateCodexWrapper(e));
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private async Task<CodexInstance> CreateCodexInstance(string endpoint)
|
||||
private CodexWrapper CreateCodexWrapper(string endpoint)
|
||||
{
|
||||
var splitIndex = endpoint.LastIndexOf(':');
|
||||
var host = endpoint.Substring(0, splitIndex);
|
||||
@ -99,35 +100,9 @@ public class Program
|
||||
port: port
|
||||
);
|
||||
|
||||
var client = new HttpClient();
|
||||
client.Timeout = TimeSpan.FromMinutes(60.0);
|
||||
var codex = new CodexApi(client);
|
||||
codex.BaseUrl = $"{address.Host}:{address.Port}/api/codex/v1";
|
||||
|
||||
app.Log.Log($"Checking Codex at {address}...");
|
||||
await CheckCodex(codex);
|
||||
app.Log.Log("OK");
|
||||
|
||||
return new CodexInstance(
|
||||
app,
|
||||
codex,
|
||||
client,
|
||||
address
|
||||
);
|
||||
}
|
||||
|
||||
private async Task CheckCodex(CodexApi codex)
|
||||
{
|
||||
try
|
||||
{
|
||||
var info = await codex.GetDebugInfoAsync();
|
||||
if (string.IsNullOrEmpty(info.Id)) throw new Exception("Failed to fetch Codex node id");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
app.Log.Error($"Codex not OK: {ex}");
|
||||
throw;
|
||||
}
|
||||
var instance = CodexInstance.CreateFromApiEndpoint("ac", address);
|
||||
var node = app.CodexNodeFactory.CreateCodexNode(instance);
|
||||
return new CodexWrapper(app, node);
|
||||
}
|
||||
|
||||
private static void PrintHelp()
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
using CodexOpenApi;
|
||||
using IdentityModel.Client;
|
||||
using CodexClient;
|
||||
using Logging;
|
||||
using Utils;
|
||||
|
||||
@ -11,15 +10,26 @@ namespace BiblioTech
|
||||
private readonly Configuration config;
|
||||
private readonly ILog log;
|
||||
private readonly Mutex checkMutex = new Mutex();
|
||||
private CodexApi? currentCodexNode;
|
||||
private readonly CodexNodeFactory factory;
|
||||
private ICodexNode? currentCodexNode;
|
||||
|
||||
public CodexCidChecker(Configuration config, ILog log)
|
||||
{
|
||||
this.config = config;
|
||||
this.log = log;
|
||||
|
||||
factory = new CodexNodeFactory(log, dataDir: config.DataPath);
|
||||
|
||||
if (!string.IsNullOrEmpty(config.CodexEndpointAuth) && config.CodexEndpointAuth.Contains(":"))
|
||||
{
|
||||
throw new Exception("Todo: codexnodefactory httpfactory support basicauth!");
|
||||
//var tokens = config.CodexEndpointAuth.Split(':');
|
||||
//if (tokens.Length != 2) throw new Exception("Expected '<username>:<password>' in CodexEndpointAuth parameter.");
|
||||
//client.SetBasicAuthentication(tokens[0], tokens[1]);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<CheckResponse> PerformCheck(string cid)
|
||||
public CheckResponse PerformCheck(string cid)
|
||||
{
|
||||
if (string.IsNullOrEmpty(config.CodexEndpoint))
|
||||
{
|
||||
@ -30,10 +40,10 @@ namespace BiblioTech
|
||||
{
|
||||
checkMutex.WaitOne();
|
||||
var codex = GetCodex();
|
||||
var nodeCheck = await CheckCodex(codex);
|
||||
var nodeCheck = CheckCodex(codex);
|
||||
if (!nodeCheck) return new CheckResponse(false, "Codex node is not available. Cannot perform check.", $"Codex node at '{config.CodexEndpoint}' did not respond correctly to debug/info.");
|
||||
|
||||
return await PerformCheck(codex, cid);
|
||||
return PerformCheck(codex, cid);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -45,19 +55,13 @@ namespace BiblioTech
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<CheckResponse> PerformCheck(CodexApi codex, string cid)
|
||||
private CheckResponse PerformCheck(ICodexNode codex, string cid)
|
||||
{
|
||||
try
|
||||
{
|
||||
var manifest = await codex.DownloadNetworkManifestAsync(cid);
|
||||
var manifest = codex.DownloadManifestOnly(new ContentId(cid));
|
||||
return SuccessMessage(manifest);
|
||||
}
|
||||
catch (ApiException apiEx)
|
||||
{
|
||||
if (apiEx.StatusCode == 400) return CidFormatInvalid(apiEx.Response);
|
||||
if (apiEx.StatusCode == 404) return FailedToFetch(apiEx.Response);
|
||||
return UnexpectedReturnCode(apiEx.Response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return UnexpectedException(ex);
|
||||
@ -66,13 +70,13 @@ namespace BiblioTech
|
||||
|
||||
#region Response formatting
|
||||
|
||||
private CheckResponse SuccessMessage(DataItem content)
|
||||
private CheckResponse SuccessMessage(LocalDataset content)
|
||||
{
|
||||
return FormatResponse(
|
||||
success: true,
|
||||
title: $"Success: '{content.Cid}'",
|
||||
error: "",
|
||||
$"size: {content.Manifest.DatasetSize} bytes",
|
||||
$"size: {content.Manifest.OriginalBytes} bytes",
|
||||
$"blockSize: {content.Manifest.BlockSize} bytes",
|
||||
$"protected: {content.Manifest.Protected}"
|
||||
);
|
||||
@ -143,17 +147,17 @@ namespace BiblioTech
|
||||
|
||||
#region Codex Node API
|
||||
|
||||
private CodexApi GetCodex()
|
||||
private ICodexNode GetCodex()
|
||||
{
|
||||
if (currentCodexNode == null) currentCodexNode = CreateCodex();
|
||||
return currentCodexNode;
|
||||
}
|
||||
|
||||
private async Task<bool> CheckCodex(CodexApi codex)
|
||||
private bool CheckCodex(ICodexNode node)
|
||||
{
|
||||
try
|
||||
{
|
||||
var info = await currentCodexNode!.GetDebugInfoAsync();
|
||||
var info = node.GetDebugInfo();
|
||||
if (info == null || string.IsNullOrEmpty(info.Id)) return false;
|
||||
return true;
|
||||
}
|
||||
@ -164,7 +168,7 @@ namespace BiblioTech
|
||||
}
|
||||
}
|
||||
|
||||
private CodexApi CreateCodex()
|
||||
private ICodexNode CreateCodex()
|
||||
{
|
||||
var endpoint = config.CodexEndpoint;
|
||||
var splitIndex = endpoint.LastIndexOf(':');
|
||||
@ -177,17 +181,8 @@ namespace BiblioTech
|
||||
port: port
|
||||
);
|
||||
|
||||
var client = new HttpClient();
|
||||
if (!string.IsNullOrEmpty(config.CodexEndpointAuth) && config.CodexEndpointAuth.Contains(":"))
|
||||
{
|
||||
var tokens = config.CodexEndpointAuth.Split(':');
|
||||
if (tokens.Length != 2) throw new Exception("Expected '<username>:<password>' in CodexEndpointAuth parameter.");
|
||||
client.SetBasicAuthentication(tokens[0], tokens[1]);
|
||||
}
|
||||
|
||||
var codex = new CodexApi(client);
|
||||
codex.BaseUrl = $"{address.Host}:{address.Port}/api/codex/v1";
|
||||
return codex;
|
||||
var instance = CodexInstance.CreateFromApiEndpoint("ac", address);
|
||||
return factory.CreateCodexNode(instance);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
using BiblioTech.Options;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using BiblioTech.Rewards;
|
||||
using System.Data;
|
||||
|
||||
namespace BiblioTech.Commands
|
||||
{
|
||||
@ -38,7 +33,7 @@ namespace BiblioTech.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
var response = await checker.PerformCheck(cid);
|
||||
var response = checker.PerformCheck(cid);
|
||||
await Program.AdminChecker.SendInAdminChannel($"User {Mention(user)} used '/{Name}' for cid '{cid}'. Lookup-success: {response.Success}. Message: '{response.Message}' Error: '{response.Error}'");
|
||||
|
||||
if (response.Success)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
using BiblioTech.Options;
|
||||
using CodexContractsPlugin;
|
||||
using GethPlugin;
|
||||
using Utils;
|
||||
|
||||
namespace BiblioTech.Commands
|
||||
{
|
||||
|
||||
@ -3,6 +3,7 @@ using Discord.WebSocket;
|
||||
using DiscordRewards;
|
||||
using Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Utils;
|
||||
|
||||
namespace BiblioTech.Rewards
|
||||
{
|
||||
@ -138,7 +139,7 @@ namespace BiblioTech.Rewards
|
||||
{
|
||||
try
|
||||
{
|
||||
var userData = Program.UserRepo.GetUserDataForAddress(new GethPlugin.EthAddress(address));
|
||||
var userData = Program.UserRepo.GetUserDataForAddress(new EthAddress(address));
|
||||
if (userData != null) log.Log($"User '{userData.Name}' was looked up.");
|
||||
else log.Log($"Lookup for user was unsuccessful. EthAddress: '{address}'");
|
||||
return userData;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using CodexContractsPlugin;
|
||||
using CodexClient;
|
||||
using CodexContractsPlugin;
|
||||
using CodexPlugin;
|
||||
using Core;
|
||||
using GethPlugin;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
using ArgsUniform;
|
||||
using CodexPlugin;
|
||||
using CodexClient;
|
||||
using DistTestCore;
|
||||
|
||||
namespace CodexNetDeployer
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using BlockchainUtils;
|
||||
using CodexClient;
|
||||
using CodexContractsPlugin;
|
||||
using CodexDiscordBotPlugin;
|
||||
using CodexPlugin;
|
||||
@ -7,6 +8,7 @@ using GethPlugin;
|
||||
using KubernetesWorkflow.Types;
|
||||
using Logging;
|
||||
using MetricsPlugin;
|
||||
using WebUtils;
|
||||
|
||||
namespace CodexNetDeployer
|
||||
{
|
||||
@ -100,7 +102,7 @@ namespace CodexNetDeployer
|
||||
retryDelay: TimeSpan.FromSeconds(10),
|
||||
kubernetesNamespace: config.KubeNamespace);
|
||||
|
||||
var result = new EntryPoint(log, configuration, string.Empty, new FastHttpTimeSet());
|
||||
var result = new EntryPoint(log, configuration, string.Empty, new FastHttpTimeSet(), new DefaultK8sTimeSet());
|
||||
configuration.Hooks = new K8sHook(config.TestsTypePodLabel, config.DeployId, result.GetPluginMetadata());
|
||||
|
||||
return result;
|
||||
@ -248,7 +250,7 @@ namespace CodexNetDeployer
|
||||
}
|
||||
}
|
||||
|
||||
public class FastHttpTimeSet : ITimeSet
|
||||
public class FastHttpTimeSet : IWebCallTimeSet
|
||||
{
|
||||
public TimeSpan HttpCallRetryDelay()
|
||||
{
|
||||
@ -264,15 +266,5 @@ namespace CodexNetDeployer
|
||||
{
|
||||
return TimeSpan.FromSeconds(10);
|
||||
}
|
||||
|
||||
public TimeSpan K8sOperationTimeout()
|
||||
{
|
||||
return TimeSpan.FromMinutes(10);
|
||||
}
|
||||
|
||||
public TimeSpan K8sOperationRetryDelay()
|
||||
{
|
||||
return TimeSpan.FromSeconds(30);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user