/ ProjectPlugins / CodexPlugin / CodexContainerRecipe.cs
CodexContainerRecipe.cs
  1  using GethPlugin;
  2  using KubernetesWorkflow;
  3  using KubernetesWorkflow.Recipe;
  4  using Utils;
  5  
  6  namespace CodexPlugin
  7  {
  8      public class CodexContainerRecipe : ContainerRecipeFactory
  9      {
 10          public const string ApiPortTag = "codex_api_port";
 11          public const string ListenPortTag = "codex_listen_port";
 12          public const string MetricsPortTag = "codex_metrics_port";
 13          public const string DiscoveryPortTag = "codex_discovery_port";
 14  
 15          // Used by tests for time-constraint assertions.
 16          public static readonly TimeSpan MaxUploadTimePerMegabyte = TimeSpan.FromSeconds(2.0);
 17          public static readonly TimeSpan MaxDownloadTimePerMegabyte = TimeSpan.FromSeconds(2.0);
 18          private readonly CodexDockerImage codexDockerImage;
 19  
 20          public override string AppName => "codex";
 21          public override string Image => codexDockerImage.GetCodexDockerImage();
 22  
 23          public CodexContainerRecipe(CodexDockerImage codexDockerImage)
 24          {
 25              this.codexDockerImage = codexDockerImage;
 26          }
 27  
 28          protected override void Initialize(StartupConfig startupConfig)
 29          {
 30              SetResourcesRequest(milliCPUs: 100, memory: 100.MB());
 31              //SetResourceLimits(milliCPUs: 4000, memory: 12.GB());
 32  
 33              SetSchedulingAffinity(notIn: "false");
 34              SetSystemCriticalPriority();
 35  
 36              var config = startupConfig.Get<CodexStartupConfig>();
 37  
 38              var apiPort = CreateApiPort(config, ApiPortTag);
 39              AddEnvVar("CODEX_API_PORT", apiPort);
 40              AddEnvVar("CODEX_API_BINDADDR", "0.0.0.0");
 41  
 42              var dataDir = $"datadir{ContainerNumber}";
 43              AddEnvVar("CODEX_DATA_DIR", dataDir);
 44              AddVolume($"codex/{dataDir}", GetVolumeCapacity(config));
 45  
 46              var discPort = CreateDiscoveryPort(config);
 47              AddEnvVar("CODEX_DISC_PORT", discPort);
 48              AddEnvVar("CODEX_LOG_LEVEL", config.LogLevelWithTopics());
 49  
 50              if (config.PublicTestNet != null)
 51              {
 52                  // This makes the node announce itself to its public IP address.
 53                  AddEnvVar("NAT_IP_AUTO", "false");
 54                  AddEnvVar("NAT_PUBLIC_IP_AUTO", PublicIpService.Address);
 55              }
 56              else
 57              {
 58                  // This makes the node announce itself to its local (pod) IP address.
 59                  AddEnvVar("NAT_IP_AUTO", "true");
 60              }
 61  
 62              var listenPort = CreateListenPort(config);
 63              AddEnvVar("CODEX_LISTEN_ADDRS", $"/ip4/0.0.0.0/tcp/{listenPort.Number}");
 64  
 65              if (!string.IsNullOrEmpty(config.BootstrapSpr))
 66              {
 67                  AddEnvVar("CODEX_BOOTSTRAP_NODE", config.BootstrapSpr);
 68              }
 69              if (config.StorageQuota != null)
 70              {
 71                  AddEnvVar("CODEX_STORAGE_QUOTA", config.StorageQuota.SizeInBytes.ToString()!);
 72              }
 73              if (config.BlockTTL != null)
 74              {
 75                  AddEnvVar("CODEX_BLOCK_TTL", config.BlockTTL.ToString()!);
 76              }
 77              if (config.BlockMaintenanceInterval != null)
 78              {
 79                  AddEnvVar("CODEX_BLOCK_MI", Convert.ToInt32(config.BlockMaintenanceInterval.Value.TotalSeconds).ToString());
 80              }
 81              if (config.BlockMaintenanceNumber != null)
 82              {
 83                  AddEnvVar("CODEX_BLOCK_MN", config.BlockMaintenanceNumber.ToString()!);
 84              }
 85              if (config.MetricsEnabled)
 86              {
 87                  var metricsPort = CreateApiPort(config, MetricsPortTag);
 88                  AddEnvVar("CODEX_METRICS", "true");
 89                  AddEnvVar("CODEX_METRICS_ADDRESS", "0.0.0.0");
 90                  AddEnvVar("CODEX_METRICS_PORT", metricsPort);
 91                  AddPodAnnotation("prometheus.io/scrape", "true");
 92                  AddPodAnnotation("prometheus.io/port", metricsPort.Number.ToString());
 93              }
 94  
 95              if (config.SimulateProofFailures != null)
 96              {
 97                  AddEnvVar("CODEX_SIMULATE_PROOF_FAILURES", config.SimulateProofFailures.ToString()!);
 98              }
 99  
100              if (config.MarketplaceConfig != null)
101              {
102                  var mconfig = config.MarketplaceConfig;
103                  var gethStart = mconfig.GethNode.StartResult;
104                  var wsAddress = gethStart.Container.GetInternalAddress(GethContainerRecipe.WsPortTag);
105                  var marketplaceAddress = mconfig.CodexContracts.Deployment.MarketplaceAddress;
106  
107                  AddEnvVar("CODEX_ETH_PROVIDER", $"{wsAddress.Host.Replace("http://", "ws://")}:{wsAddress.Port}");
108                  AddEnvVar("CODEX_MARKETPLACE_ADDRESS", marketplaceAddress);
109  
110                  var marketplaceSetup = config.MarketplaceConfig.MarketplaceSetup;
111  
112                  // Custom scripting in the Codex test image will write this variable to a private-key file,
113                  // and pass the correct filename to Codex.
114                  var account = marketplaceSetup.EthAccountSetup.GetNew();
115                  AddEnvVar("ETH_PRIVATE_KEY", account.PrivateKey);
116                  Additional(account);
117  
118                  SetCommandOverride(marketplaceSetup);
119                  if (marketplaceSetup.IsValidator)
120                  {
121                     AddEnvVar("CODEX_VALIDATOR", "true");
122                  }
123              }
124  
125              if (!string.IsNullOrEmpty(config.NameOverride))
126              {
127                  AddEnvVar("CODEX_NODENAME", config.NameOverride);
128              }
129          }
130  
131          private void SetCommandOverride(MarketplaceSetup ms)
132          {
133              if (ms.IsStorageNode)
134              {
135                  OverrideCommand("bash", "/docker-entrypoint.sh", "codex", "persistence", "prover");
136              }
137              else
138              {
139                  OverrideCommand("bash", "/docker-entrypoint.sh", "codex", "persistence");
140              }
141          }
142  
143          private Port CreateApiPort(CodexStartupConfig config, string tag)
144          {
145              if (config.PublicTestNet == null) return AddExposedPort(tag);
146              return AddInternalPort(tag);
147          }
148  
149          private Port CreateListenPort(CodexStartupConfig config)
150          {
151              if (config.PublicTestNet == null) return AddInternalPort(ListenPortTag);
152  
153              return AddExposedPort(config.PublicTestNet.PublicListenPort, ListenPortTag);
154          }
155  
156          private Port CreateDiscoveryPort(CodexStartupConfig config)
157          {
158              if (config.PublicTestNet == null) return AddInternalPort(DiscoveryPortTag, PortProtocol.UDP);
159  
160              return AddExposedPort(config.PublicTestNet.PublicDiscoveryPort, DiscoveryPortTag, PortProtocol.UDP);
161          }
162  
163          private ByteSize GetVolumeCapacity(CodexStartupConfig config)
164          {
165              if (config.StorageQuota != null) return config.StorageQuota.Multiply(1.2);
166              // Default Codex quota: 8 Gb, using +20% to be safe.
167              return 8.GB().Multiply(1.2);
168          }
169      }
170  }