GethContainerInfoExtractor.cs
1 using KubernetesWorkflow; 2 using KubernetesWorkflow.Types; 3 using Logging; 4 using Utils; 5 6 namespace GethPlugin 7 { 8 public class GethContainerInfoExtractor 9 { 10 private readonly ILog log; 11 private readonly IStartupWorkflow workflow; 12 private readonly RunningContainer container; 13 14 public GethContainerInfoExtractor(ILog log, IStartupWorkflow workflow, RunningContainer container) 15 { 16 this.log = log; 17 this.workflow = workflow; 18 this.container = container; 19 } 20 21 public AllGethAccounts ExtractAccounts() 22 { 23 log.Debug(); 24 var accountsCsv = Retry(() => FetchAccountsCsv()); 25 if (string.IsNullOrEmpty(accountsCsv)) throw new InvalidOperationException("Unable to fetch accounts.csv for geth node. Test infra failure."); 26 27 var lines = accountsCsv.Split('\n'); 28 return new AllGethAccounts(lines.Select(ParseLineToAccount).ToArray()); 29 } 30 31 public string ExtractPubKey() 32 { 33 log.Debug(); 34 var pubKey = Retry(FetchPubKey); 35 if (string.IsNullOrEmpty(pubKey)) throw new InvalidOperationException("Unable to fetch enode from geth node. Test infra failure."); 36 37 return pubKey; 38 } 39 40 private string FetchAccountsCsv() 41 { 42 return workflow.ExecuteCommand(container, "cat", GethContainerRecipe.AccountsFilename); 43 } 44 45 private string FetchPubKey() 46 { 47 var enodeFinder = new PubKeyFinder(s => log.Debug(s)); 48 workflow.DownloadContainerLog(container, enodeFinder, null); 49 return enodeFinder.GetPubKey(); 50 } 51 52 private GethAccount ParseLineToAccount(string l) 53 { 54 var tokens = l.Replace("\r", "").Split(','); 55 if (tokens.Length != 2) throw new InvalidOperationException(); 56 var account = tokens[0]; 57 var privateKey = tokens[1]; 58 return new GethAccount(account, privateKey); 59 } 60 61 private static string Retry(Func<string> fetch) 62 { 63 // This class is the first moment where we interact with our new geth container. 64 // K8s might be moving pods and/or setting up new VMs. 65 // So we apply a generous retry timeout. 66 var retry = new Retry(nameof(GethContainerInfoExtractor), 67 maxTimeout: TimeSpan.FromMinutes(15.0), 68 sleepAfterFail: TimeSpan.FromSeconds(20.0), 69 onFail: f => { }, 70 failFast: false); 71 72 return retry.Run(fetch); 73 } 74 } 75 76 public class PubKeyFinder : LogHandler, ILogHandler 77 { 78 private const string openTag = "self=enode://"; 79 private const string openTagQuote = "self=\"enode://"; 80 private readonly Action<string> debug; 81 private string pubKey = string.Empty; 82 83 public PubKeyFinder(Action<string> debug) 84 { 85 this.debug = debug; 86 debug($"Looking for '{openTag}' in container logs..."); 87 } 88 89 public string GetPubKey() 90 { 91 if (string.IsNullOrEmpty(pubKey)) throw new Exception("Not found yet exception."); 92 return pubKey; 93 } 94 95 protected override void ProcessLine(string line) 96 { 97 debug(line); 98 if (line.Contains(openTag)) 99 { 100 ExtractPubKey(openTag, line); 101 } 102 else if (line.Contains(openTagQuote)) 103 { 104 ExtractPubKey(openTagQuote, line); 105 } 106 } 107 108 private void ExtractPubKey(string tag, string line) 109 { 110 var openIndex = line.IndexOf(tag) + tag.Length; 111 var closeIndex = line.IndexOf("@"); 112 113 pubKey = line.Substring( 114 startIndex: openIndex, 115 length: closeIndex - openIndex); 116 } 117 } 118 }