diff --git a/CodexNetDeployer/ArgOrVar.cs b/CodexNetDeployer/ArgOrVar.cs new file mode 100644 index 0000000..df70171 --- /dev/null +++ b/CodexNetDeployer/ArgOrVar.cs @@ -0,0 +1,89 @@ +namespace CodexNetDeployer +{ + public class ArgOrVar + { + public static readonly ArgVar CodexImage = new ArgVar("codex-image", "CODEXIMAGE", "Docker image of Codex."); + public static readonly ArgVar GethImage = new ArgVar("geth-image", "GETHIMAGE", "Docker image of Geth."); + public static readonly ArgVar ContractsImage = new ArgVar("contracts-image", "CONTRACTSIMAGE", "Docker image of Codex Contracts deployer."); + public static readonly ArgVar KubeConfigFile = new ArgVar("kube-config", "KUBECONFIG", "Path to Kubeconfig file."); + public static readonly ArgVar KubeNamespace = new ArgVar("kube-namespace", "KUBENAMESPACE", "Kubernetes namespace to be used for deployment."); + public static readonly ArgVar NumberOfCodexNodes = new ArgVar("nodes", "NODES", "Number of Codex nodes to be created."); + public static readonly ArgVar StorageQuota = new ArgVar("storage-quota", "STORAGEQUOTA", "Storage quota in megabytes used by each Codex node."); + + private readonly string[] args; + + public ArgOrVar(string[] args) + { + this.args = args; + } + + public string Get(ArgVar key, string defaultValue = "") + { + var argKey = $"--{key.Arg}="; + var arg = args.FirstOrDefault(a => a.StartsWith(argKey)); + if (arg != null) + { + return arg.Substring(argKey.Length); + } + + var env = Environment.GetEnvironmentVariable(key.Var); + if (env != null) + { + return env; + } + + return defaultValue; + } + + public int? GetInt(ArgVar key) + { + var str = Get(key); + if (string.IsNullOrEmpty(str)) return null; + if (int.TryParse(str, out int result)) + { + return result; + } + return null; + } + + public void PrintHelp() + { + var nl = Environment.NewLine; + Console.WriteLine("CodexNetDeployer allows you to easily deploy multiple Codex nodes in a Kubernetes cluster. " + + "The deployer will set up the required supporting services, deploy the Codex on-chain contracts, start and bootstrap the Codex instances. " + + "All Kubernetes objects will be created in the namespace provided, allowing you to easily find, modify, and delete them afterwards." + nl); + + + Console.Write("\t[ CLI argument ] or [ Environment variable ]"); + Console.CursorLeft = 70; + Console.Write("(Description)" + nl); + var fields = GetType().GetFields();// System.Reflection.BindingFlags.Public & System.Reflection.BindingFlags.Static); + foreach (var field in fields) + { + var value = (ArgVar)field.GetValue(null)!; + value.PrintHelp(); + } + } + } + + public class ArgVar + { + public ArgVar(string arg, string var, string description) + { + Arg = arg; + Var = var; + Description = description; + } + + public string Arg { get; } + public string Var { get; } + public string Description { get; } + + public void PrintHelp() + { + Console.Write($"\t[ --{Arg}=... ] or [ {Var}=... ]"); + Console.CursorLeft = 70; + Console.Write(Description + Environment.NewLine); + } + } +} diff --git a/CodexNetDeployer/CodexNetDeployer.csproj b/CodexNetDeployer/CodexNetDeployer.csproj index f02677b..80f6f4c 100644 --- a/CodexNetDeployer/CodexNetDeployer.csproj +++ b/CodexNetDeployer/CodexNetDeployer.csproj @@ -7,4 +7,8 @@ enable + + + + diff --git a/CodexNetDeployer/Configuration.cs b/CodexNetDeployer/Configuration.cs new file mode 100644 index 0000000..3abb7a4 --- /dev/null +++ b/CodexNetDeployer/Configuration.cs @@ -0,0 +1,84 @@ +namespace CodexNetDeployer +{ + public class Configuration + { + public Configuration( + string codexImage, + string gethImage, + string contractsImage, + string kubeConfigFile, + string kubeNamespace, + int? numberOfCodexNodes, + int? storageQuota) + { + CodexImage = codexImage; + GethImage = gethImage; + ContractsImage = contractsImage; + KubeConfigFile = kubeConfigFile; + KubeNamespace = kubeNamespace; + NumberOfCodexNodes = numberOfCodexNodes; + StorageQuota = storageQuota; + } + + public string CodexImage { get; } + public string GethImage { get; } + public string ContractsImage { get; } + public string KubeConfigFile { get; } + public string KubeNamespace { get; } + public int? NumberOfCodexNodes { get; } + public int? StorageQuota { get; } + + public void PrintConfig() + { + ForEachProperty(onString: Print, onInt: Print); + } + + public List Validate() + { + var errors = new List(); + + ForEachProperty( + onString: (n, v) => StringIsSet(n, v, errors), + onInt: (n, v) => IntIsOverZero(n, v, errors)); + + return errors; + } + + private void ForEachProperty(Action onString, Action onInt) + { + var properties = GetType().GetProperties(); + foreach (var p in properties) + { + if (p.PropertyType == typeof(string)) onString(p.Name, (string)p.GetValue(this)!); + if (p.PropertyType == typeof(int?)) onInt(p.Name, (int?)p.GetValue(this)!); + } + } + + private static void IntIsOverZero(string variable, int? value, List errors) + { + if (value == null || value.Value < 1) + { + errors.Add($"{variable} is must be set and must be greater than 0."); + } + } + + private static void StringIsSet(string variable, string value, List errors) + { + if (string.IsNullOrWhiteSpace(value)) + { + errors.Add($"{variable} is must be set."); + } + } + + private static void Print(string variable, string value) + { + Console.WriteLine($"\t{variable}: '{value}'"); + } + + private static void Print(string variable, int? value) + { + if (value != null) Print(variable, value.ToString()!); + else Print(variable, ""); + } + } +} diff --git a/CodexNetDeployer/Program.cs b/CodexNetDeployer/Program.cs index a08476f..64a445f 100644 --- a/CodexNetDeployer/Program.cs +++ b/CodexNetDeployer/Program.cs @@ -1,7 +1,46 @@ -public class Program +using CodexNetDeployer; +using DistTestCore.Codex; +using DistTestCore.Marketplace; + +public class Program { public static void Main(string[] args) { - Console.WriteLine("Hello, World!"); + var nl = Environment.NewLine; + Console.WriteLine("CodexNetDeployer" + nl + nl); + + var argOrVar = new ArgOrVar(args); + + if (args.Any(a => a == "-h" || a == "--help" || a == "-?")) + { + argOrVar.PrintHelp(); + return; + } + + var config = new Configuration( + codexImage: argOrVar.Get(ArgOrVar.CodexImage, CodexContainerRecipe.DockerImage), + gethImage: argOrVar.Get(ArgOrVar.GethImage, GethContainerRecipe.DockerImage), + contractsImage: argOrVar.Get(ArgOrVar.ContractsImage, CodexContractsContainerRecipe.DockerImage), + kubeConfigFile: argOrVar.Get(ArgOrVar.KubeConfigFile), + kubeNamespace: argOrVar.Get(ArgOrVar.KubeNamespace), + numberOfCodexNodes: argOrVar.GetInt(ArgOrVar.NumberOfCodexNodes), + storageQuota: argOrVar.GetInt(ArgOrVar.StorageQuota) + ); + + Console.WriteLine("Using:"); + config.PrintConfig(); + Console.WriteLine(nl); + + var errors = config.Validate(); + if (errors.Any()) + { + Console.WriteLine($"Configuration errors: ({errors.Count})"); + foreach ( var error in errors ) Console.WriteLine("\t" + error); + Console.WriteLine(nl); + argOrVar.PrintHelp(); + return; + } + + } }