using CommandLine; using Coscine.GraphDeployer.Models.ConfigurationModels; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using NLog; using NLog.Extensions.Logging; using Winton.Extensions.Configuration.Consul; using static Coscine.GraphDeployer.Utils.CommandLineOptions; using LogLevel = Microsoft.Extensions.Logging.LogLevel; namespace Coscine.GraphDeployer; public class Program { private static IServiceProvider _serviceProvider = null!; private static string? _environmentName; public static int Main(string[] args) { _environmentName = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT"); Console.WriteLine($"Running in environment: {_environmentName ?? "environment is null"}"); InitializeServices(); var logger = _serviceProvider.GetRequiredService<ILogger<Program>>(); try { var parserResult = Parser.Default.ParseArguments<GraphDeployerOptions>(args); var result = parserResult.MapResult( (opts) => _serviceProvider.GetRequiredService<Deployer>().RunAsync(opts).Result, HandleParseError ); if (result) { logger.LogInformation("Finished."); return 0; // Exit Code 0 for Success } else { logger.LogInformation("Program execution was interrupted."); return -1; // Exit Code -1 for Failure } } catch (Exception ex) { logger.LogError(ex, "Exception: {message}", ex.Message); return -1; // Exit Code -1 for Failure } finally { DisposeServices(); } } private static void InitializeServices() { // Create a new instance of ConfigurationBuilder var configBuilder = new ConfigurationBuilder(); // Define the Consul URL var consulUrl = Environment.GetEnvironmentVariable("CONSUL_URL") ?? "http://localhost:8500"; // Remove the default sources configBuilder.Sources.Clear(); // Add Consul as a configuration source var configuration = configBuilder .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.{_environmentName}.json", optional: true, reloadOnChange: true) .AddConsul( "coscine/Coscine.Infrastructure/GraphDeployer/appsettings", options => { options.ConsulConfigurationOptions = cco => cco.Address = new Uri(consulUrl); options.Optional = true; options.ReloadOnChange = true; options.PollWaitTime = TimeSpan.FromSeconds(5); options.OnLoadException = exceptionContext => exceptionContext.Ignore = true; } ) .AddConsul( $"coscine/Coscine.Infrastructure/GraphDeployer/appsettings.{_environmentName}.json", options => { options.ConsulConfigurationOptions = cco => cco.Address = new Uri(consulUrl); options.Optional = true; options.ReloadOnChange = true; options.PollWaitTime = TimeSpan.FromSeconds(5); options.OnLoadException = exceptionContext => exceptionContext.Ignore = true; } ) .AddEnvironmentVariables() .Build(); var services = new ServiceCollection() .AddSingleton<IConfiguration>(configuration); // Add the configuration to the service collection services.Configure<GraphDeployerConfiguration>(config => { configuration.GetSection(GraphDeployerConfiguration.Section).Bind(config); }); // Add logging services.AddLogging(builder => { builder.ClearProviders(); builder.SetMinimumLevel(LogLevel.Trace); builder.AddNLog(); }); var graphDeployerConfiguration = new GraphDeployerConfiguration(); configuration.Bind(GraphDeployerConfiguration.Section, graphDeployerConfiguration); if (graphDeployerConfiguration.Endpoint is null) { throw new ArgumentNullException(nameof(graphDeployerConfiguration.Endpoint), "Endpoint cannot be null."); } if (graphDeployerConfiguration.ApiKey is null) { throw new ArgumentNullException(nameof(graphDeployerConfiguration.ApiKey), "ApiKey cannot be null."); } // Set the default LogLevel LogManager.Configuration.Variables["logLevel"] = graphDeployerConfiguration.Logger?.LogLevel ?? "Trace"; // Set the log location LogManager.Configuration.Variables["logHome"] = graphDeployerConfiguration.Logger?.LogHome ?? Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs"); // Register the HTTP client services.AddHttpClient(); // Add services for reporting services.AddTransient<Deployer>(); _serviceProvider = services.BuildServiceProvider(); } private static void DisposeServices() { if (_serviceProvider == null) { return; } if (_serviceProvider is IDisposable disposable) { disposable.Dispose(); } } private static bool HandleParseError(IEnumerable<Error> errs) { var logger = _serviceProvider.GetService<ILogger<Program>>(); foreach (var err in errs) { if (err is HelpRequestedError || err is VersionRequestedError) { // Handle the display of help or version information // Usually, the library will automatically display the help/version info, // but you can customize it if needed. } else { // For other types of errors, you can log them or write them to the console logger?.LogError("Error encountered parsing command-line options: {Error}", err.ToString()); Console.Error.WriteLine($"Error: {err}"); } } // Since there were errors, we typically return false to indicate that the program should not proceed return false; } public static TResult SanitizeOptions<TResult>(TResult unsanitizedOptions) { // Sanitize all input that accepts an array or is a list of inputs. if (unsanitizedOptions is not null) { var type = unsanitizedOptions.GetType(); if (type == typeof(GraphDeployerOptions)) { var options = unsanitizedOptions as GraphDeployerOptions; if (options is not null) { // Sanitize options here return (TResult)(object)options; } } } return unsanitizedOptions; } }