2023-09-12 11:32:06 +00:00
using Core ;
2023-09-11 08:43:27 +00:00
using FileUtils ;
2023-09-19 09:51:59 +00:00
using GethPlugin ;
2023-09-13 10:24:46 +00:00
using KubernetesWorkflow ;
2023-09-12 09:43:46 +00:00
using Logging ;
2023-09-13 09:59:21 +00:00
using MetricsPlugin ;
2023-04-13 07:33:10 +00:00
using NUnit.Framework ;
2023-08-02 13:11:27 +00:00
using Utils ;
2023-04-13 07:33:10 +00:00
2023-09-11 09:59:33 +00:00
namespace CodexPlugin
2023-04-13 07:33:10 +00:00
{
2023-09-19 09:51:59 +00:00
public interface ICodexNode : IHasContainer , IHasMetricsScrapeTarget , IHasEthAddress
2023-04-13 07:33:10 +00:00
{
2023-04-19 07:19:06 +00:00
string GetName ( ) ;
2023-04-13 07:33:10 +00:00
CodexDebugResponse GetDebugInfo ( ) ;
2023-05-11 10:44:53 +00:00
CodexDebugPeerResponse GetDebugPeer ( string peerId ) ;
2023-09-12 11:32:06 +00:00
ContentId UploadFile ( TrackedFile file ) ;
TrackedFile ? DownloadContent ( ContentId contentId , string fileLabel = "" ) ;
2023-09-19 09:51:59 +00:00
void ConnectToPeer ( ICodexNode node ) ;
2023-07-31 09:51:29 +00:00
CodexDebugVersionResponse Version { get ; }
2023-09-19 09:51:59 +00:00
void Stop ( ) ;
2023-04-13 07:33:10 +00:00
}
2023-09-19 09:51:59 +00:00
public class CodexNode : ICodexNode
2023-04-13 07:33:10 +00:00
{
private const string SuccessfullyConnectedMessage = "Successfully connected to peer" ;
private const string UploadFailedMessage = "Unable to store block" ;
2023-09-12 09:43:46 +00:00
private readonly IPluginTools tools ;
2023-09-19 09:51:59 +00:00
private readonly IEthAddress ? ethAddress ;
2023-04-13 07:33:10 +00:00
2023-09-19 09:51:59 +00:00
public CodexNode ( IPluginTools tools , CodexAccess codexAccess , CodexNodeGroup group , IEthAddress ? ethAddress )
2023-04-13 07:33:10 +00:00
{
2023-09-12 09:43:46 +00:00
this . tools = tools ;
2023-09-19 09:51:59 +00:00
this . ethAddress = ethAddress ;
2023-04-13 07:33:10 +00:00
CodexAccess = codexAccess ;
Group = group ;
2023-07-31 09:51:29 +00:00
Version = new CodexDebugVersionResponse ( ) ;
2023-04-13 07:33:10 +00:00
}
2023-09-13 10:24:46 +00:00
public RunningContainer Container { get { return CodexAccess . Container ; } }
2023-04-13 07:33:10 +00:00
public CodexAccess CodexAccess { get ; }
public CodexNodeGroup Group { get ; }
2023-07-31 09:51:29 +00:00
public CodexDebugVersionResponse Version { get ; private set ; }
2023-09-13 09:59:21 +00:00
public IMetricsScrapeTarget MetricsScrapeTarget
{
get
{
var port = CodexAccess . Container . Recipe . GetPortByTag ( CodexContainerRecipe . MetricsPortTag ) ;
if ( port = = null ) throw new Exception ( "Metrics is not available for this Codex node. Please start it with the option '.EnableMetrics()' to enable it." ) ;
return new MetricsScrapeTarget ( CodexAccess . Container , port ) ;
}
}
2023-09-19 09:51:59 +00:00
public IEthAddress EthAddress
{
get
{
if ( ethAddress = = null ) throw new Exception ( "Marketplace is not enabled for this Codex node. Please start it with the option '.EnableMarketplace(...)' to enable it." ) ;
return ethAddress ;
}
}
2023-04-13 07:33:10 +00:00
public string GetName ( )
{
2023-04-30 08:56:19 +00:00
return CodexAccess . Container . Name ;
2023-04-13 07:33:10 +00:00
}
public CodexDebugResponse GetDebugInfo ( )
{
2023-06-29 14:07:49 +00:00
var debugInfo = CodexAccess . GetDebugInfo ( ) ;
2023-05-18 08:42:04 +00:00
var known = string . Join ( "," , debugInfo . table . nodes . Select ( n = > n . peerId ) ) ;
Log ( $"Got DebugInfo with id: '{debugInfo.id}'. This node knows: {known}" ) ;
2023-04-19 12:57:00 +00:00
return debugInfo ;
2023-04-13 07:33:10 +00:00
}
2023-05-11 10:44:53 +00:00
public CodexDebugPeerResponse GetDebugPeer ( string peerId )
{
2023-06-29 14:07:49 +00:00
return CodexAccess . GetDebugPeer ( peerId ) ;
2023-05-11 10:44:53 +00:00
}
2023-09-12 11:32:06 +00:00
public ContentId UploadFile ( TrackedFile file )
2023-04-13 07:33:10 +00:00
{
2023-09-12 09:43:46 +00:00
using var fileStream = File . OpenRead ( file . Filename ) ;
2023-06-07 06:30:10 +00:00
2023-09-12 09:43:46 +00:00
var logMessage = $"Uploading file {file.Describe()}..." ;
Log ( logMessage ) ;
var response = Stopwatch . Measure ( tools . GetLog ( ) , logMessage , ( ) = >
{
return CodexAccess . UploadFile ( fileStream ) ;
} ) ;
2023-06-07 06:30:10 +00:00
2023-09-12 09:43:46 +00:00
if ( string . IsNullOrEmpty ( response ) ) Assert . Fail ( "Received empty response." ) ;
if ( response . StartsWith ( UploadFailedMessage ) ) Assert . Fail ( "Node failed to store block." ) ;
2023-07-11 06:19:14 +00:00
2023-09-12 09:43:46 +00:00
Log ( $"Uploaded file. Received contentId: '{response}'." ) ;
return new ContentId ( response ) ;
2023-04-13 07:33:10 +00:00
}
2023-09-12 11:32:06 +00:00
public TrackedFile ? DownloadContent ( ContentId contentId , string fileLabel = "" )
2023-04-13 07:33:10 +00:00
{
2023-09-12 09:43:46 +00:00
var logMessage = $"Downloading for contentId: '{contentId.Id}'..." ;
Log ( logMessage ) ;
2023-09-13 08:03:11 +00:00
var file = tools . GetFileManager ( ) . CreateEmptyFile ( fileLabel ) ;
2023-09-12 09:43:46 +00:00
Stopwatch . Measure ( tools . GetLog ( ) , logMessage , ( ) = > DownloadToFile ( contentId . Id , file ) ) ;
Log ( $"Downloaded file {file.Describe()} to '{file.Filename}'." ) ;
return file ;
2023-04-13 07:33:10 +00:00
}
2023-09-19 09:51:59 +00:00
public void ConnectToPeer ( ICodexNode node )
2023-04-13 07:33:10 +00:00
{
2023-09-19 09:51:59 +00:00
var peer = ( CodexNode ) node ;
2023-04-13 07:33:10 +00:00
Log ( $"Connecting to peer {peer.GetName()}..." ) ;
var peerInfo = node . GetDebugInfo ( ) ;
2023-06-29 14:07:49 +00:00
var response = CodexAccess . ConnectToPeer ( peerInfo . id , GetPeerMultiAddress ( peer , peerInfo ) ) ;
2023-04-13 07:33:10 +00:00
Assert . That ( response , Is . EqualTo ( SuccessfullyConnectedMessage ) , "Unable to connect codex nodes." ) ;
Log ( $"Successfully connected to peer {peer.GetName()}." ) ;
}
2023-09-19 09:51:59 +00:00
public void Stop ( )
2023-04-25 10:52:11 +00:00
{
2023-05-04 12:55:39 +00:00
if ( Group . Count ( ) > 1 ) throw new InvalidOperationException ( "Codex-nodes that are part of a group cannot be " +
"individually shut down. Use 'BringOffline()' on the group object to stop the group. This method is only " +
"available for codex-nodes in groups of 1." ) ;
2023-09-11 14:57:57 +00:00
Group . BringOffline ( ) ;
2023-04-25 10:52:11 +00:00
}
2023-07-31 09:51:29 +00:00
public void EnsureOnlineGetVersionResponse ( )
{
2023-08-02 13:11:27 +00:00
var debugInfo = Time . Retry ( CodexAccess . GetDebugInfo , "ensure online" ) ;
2023-07-31 09:51:29 +00:00
var nodePeerId = debugInfo . id ;
var nodeName = CodexAccess . Container . Name ;
if ( ! debugInfo . codex . IsValid ( ) )
{
throw new Exception ( $"Invalid version information received from Codex node {GetName()}: {debugInfo.codex}" ) ;
}
2023-09-11 09:59:33 +00:00
//lifecycle.Log.AddStringReplace(nodePeerId, nodeName);
//lifecycle.Log.AddStringReplace(debugInfo.table.localNode.nodeId, nodeName);
2023-07-31 09:51:29 +00:00
Version = debugInfo . codex ;
}
2023-09-19 09:51:59 +00:00
private string GetPeerMultiAddress ( CodexNode peer , CodexDebugResponse peerInfo )
2023-04-13 07:33:10 +00:00
{
var multiAddress = peerInfo . addrs . First ( ) ;
// Todo: Is there a case where First address in list is not the way?
// The peer we want to connect is in a different pod.
// We must replace the default IP with the pod IP in the multiAddress.
2023-08-07 08:44:48 +00:00
return multiAddress . Replace ( "0.0.0.0" , peer . CodexAccess . Container . Pod . PodInfo . Ip ) ;
2023-04-13 07:33:10 +00:00
}
2023-09-12 11:32:06 +00:00
private void DownloadToFile ( string contentId , TrackedFile file )
2023-04-13 07:33:10 +00:00
{
using var fileStream = File . OpenWrite ( file . Filename ) ;
2023-04-19 12:57:00 +00:00
try
{
2023-06-29 14:07:49 +00:00
using var downloadStream = CodexAccess . DownloadFile ( contentId ) ;
2023-04-19 12:57:00 +00:00
downloadStream . CopyTo ( fileStream ) ;
}
catch
{
Log ( $"Failed to download file '{contentId}'." ) ;
throw ;
}
2023-04-13 07:33:10 +00:00
}
private void Log ( string msg )
{
2023-09-12 09:43:46 +00:00
tools . GetLog ( ) . Log ( $"{GetName()}: {msg}" ) ;
2023-04-13 07:33:10 +00:00
}
}
public class ContentId
{
public ContentId ( string id )
{
Id = id ;
}
public string Id { get ; }
}
}