示例#1
0
        static void Main(string[] args)
        {
            var host = new HostBuilder()
                       .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.AddJsonFile("appsettings.json", true, true)
                .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment}.json", true, true)
                .AddEnvironmentVariables("VANGUARD_")
                .AddCommandLine(args)
                .Build();
            })
                       .ConfigureServices((hostingContext, services) =>
            {
                NodeOptions nodeOptions;
                if ((string)RegistryHelper.GetVanguardKey().GetValue("NodeInstalled") == "yes")
                {
                    nodeOptions = new NodeOptions
                    {
                        IsInstalled                     = true,
                        NodeName                        = RegistryHelper.GetVanguardKey().GetValue("NodeName") as string,
                        CoreConnectionHostname          = RegistryHelper.GetVanguardKey().GetValue("CoreConnectionHostname") as string,
                        CoreConnectionNoSsl             = (string)RegistryHelper.GetVanguardKey().GetValue("CoreConnectionNoSsl") == "yes",
                        CoreConnectionIgnoreSslWarnings = (string)RegistryHelper.GetVanguardKey().GetValue("CoreConnectionIgnoreSslWarnings") == "yes"
                    };
                }
                else
                {
                    nodeOptions = new NodeOptions();
                }
                services.AddSingleton(nodeOptions);

                services.AddSingleton <LocalCredentialsProvider>();
                services.AddSingleton <Authenticator>();
                services.AddTransient <NodeHttpClient>();
                services.AddSingleton <IHostedService, AuthService>();
                services.AddSingleton <IHostedService, NodeService>();
            })
                       .ConfigureLogging((hostingContext, logging) =>
            {
                logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                logging.AddConsole();
                logging.AddDebug();
            })
                       .UseConsoleLifetime()
                       .Build();

            var logger      = host.Services.GetService <ILoggerFactory>().CreateLogger("Main");
            var hostOptions = host.Services.GetService <NodeOptions>();

            var app = new CommandLineApplication {
                Name = "Vanguard Server Manager Node"
            };

            app.HelpOption("-?|-h|--help", true);

            app.Command("install", command =>
            {
                var nodeNameOption     = command.Option <string>("-n|--name", "Name of the server manager node", CommandOptionType.SingleValue);
                var coreHostnameOption = command.Option <string>("-a|--address", "Hostname or IP address of the server manager core server", CommandOptionType.SingleValue);
                var certificateFile    = command.Option <string>("-c|--certificate", "Provide a certificate instead of generating a self-signed one", CommandOptionType.SingleValue);
                var certificateKeyFile = command.Option <string>("-k|--key-file", "Provide a certificate key file instead of prompting for password for the provided certificate file", CommandOptionType.SingleValue);
                var useHttp            = command.Option <bool>("--no-ssl", "Don't use HTTPS for core communication", CommandOptionType.NoValue);
                var useInsecure        = command.Option <bool>("--insecure", "Ignore SSL warnings and errors", CommandOptionType.NoValue);

                command.OnExecute(async() =>
                {
                    try
                    {
                        if (hostOptions.IsInstalled)
                        {
                            logger.LogInformation("The service has already been installed");
                            return(0);
                        }

                        var nodeName     = nodeNameOption.HasValue() ? nodeNameOption.ParsedValue : Prompt.GetString("Name of the server manager node (Must be unique):");
                        var coreHostname = coreHostnameOption.HasValue() ? coreHostnameOption.ParsedValue : Prompt.GetString("Hostname or IP address of the server manager core server:");
                        var coreUsername = Prompt.GetString("Username for the registration:");
                        var corePassword = Prompt.GetPassword("Password for the registration:");

                        X509Certificate2 certificate;
                        if (certificateFile.HasValue())
                        {
                            certificate = EncryptionHelpers.LoadServerNodeCertificate(certificateFile.ParsedValue, certificateKeyFile.ParsedValue);
                        }
                        else
                        {
                            // TODO: Check for generated certificate and load it

                            var password        = Prompt.GetPassword("Provide the certificate password:"******"Confirm the certificate password:"******"Passwords don't match");
                            }

                            certificate = EncryptionHelpers.GenerateServerNodeCertificate(nodeName, password);
                        }

                        var nodeOptions = new NodeOptions
                        {
                            CoreConnectionHostname          = coreHostname,
                            CoreConnectionNoSsl             = useHttp.HasValue(),
                            CoreConnectionIgnoreSslWarnings = useInsecure.HasValue()
                        };
                        var authenticator = new Authenticator(nodeOptions);
                        try
                        {
                            await authenticator.AuthenticateAsync(coreUsername, corePassword);
                        }
                        catch (Exception ex)
                        {
                            logger.LogError("Failed to authenticate for installation: {0}", ex);
                            return(1);
                        }

                        using (var client = new NodeHttpClient(authenticator, nodeOptions))
                        {
                            var registrationPayload = JsonConvert.SerializeObject(new ServerNodeViewModel
                            {
                                Name      = nodeName,
                                PublicKey = certificate.GetPublicKeyString()
                            }, Formatting.None, new JsonSerializerSettings {
                                ContractResolver = new CamelCasePropertyNamesContractResolver()
                            });
                            logger.LogInformation("Starting node registration");
                            logger.LogDebug("Attempting to register the node");
                            logger.LogTrace(registrationPayload);
                            var registrationResponse = await client.PostAsync($"{nodeOptions.ApiRoot}/api/node", new StringContent(registrationPayload, Encoding.Default, "application/json"));
                            if (!registrationResponse.IsSuccessStatusCode)
                            {
                                logger.LogError("Failed to register the node: [{0}] {1}", registrationResponse.StatusCode, await registrationResponse.Content.ReadAsStringAsync());
                                return(1);
                            }

                            var credentials = JsonConvert.DeserializeObject <UsernamePasswordCredentialsViewModel>(await registrationResponse.Content.ReadAsStringAsync());
                            var localCredentialsProvider = host.Services.GetService <LocalCredentialsProvider>();
                            try
                            {
                                await localCredentialsProvider.SetCredentialsAsync("CoreConnectionCredentials", credentials);
                            }
                            catch (CredentialProviderException ex)
                            {
                                // TODO: Implement recovery action via option flag to generate a new password
                                logger.LogError("Failed to store retrieved credentials: {0}", ex);
                                return(1);
                            }

                            RegistryHelper.GetVanguardKey().SetValue("NodeInstalled", "yes");
                            RegistryHelper.GetVanguardKey().SetValue("NodeName", nodeName);
                            RegistryHelper.GetVanguardKey().SetValue("CoreConnectionHostname", coreHostname);
                            RegistryHelper.GetVanguardKey().SetValue("CoreConnectionNoSsl", useHttp.HasValue() ? "yes" : "no");
                            RegistryHelper.GetVanguardKey().SetValue("CoreConnectionIgnoreSslWarnings", useInsecure.HasValue() ? "yes" : "no");
                        }
                    }
                    catch (Exception ex)
                    {
                        logger.LogError("Install failed due to an unexpected exception: {0}", ex);
                        throw;
                    }

                    return(0);
                });
            });

            app.Command("start", command =>
            {
                var foregroundSwitch = command.Option("-f|--foreground", "Run the service in foreground mode", CommandOptionType.NoValue);

                command.OnExecute(async() =>
                {
                    using (host)
                    {
                        var nodeOptions = host.Services.GetService <NodeOptions>();
                        if (!nodeOptions.IsInstalled)
                        {
                            logger.LogInformation("The service has not been installed yet");
                            return(1);
                        }

                        try
                        {
                            if (foregroundSwitch.HasValue())
                            {
                                // TODO: Remove when child process kill has been fixed. See https://github.com/dotnet/cli/issues/7426
                                var cancellationTokenSource          = new CancellationTokenSource();
                                AppDomain.CurrentDomain.ProcessExit += (sender, eventArgs) =>
                                {
                                    cancellationTokenSource.Cancel();
                                };
                                Console.CancelKeyPress += (sender, e) =>
                                {
                                    e.Cancel = true;
                                    cancellationTokenSource.Cancel();
                                };

                                await host.StartAsync(cancellationTokenSource.Token);
                                await host.WaitForShutdownAsync(cancellationTokenSource.Token);
                                //await host.StartAsync();
                                //await host.WaitForShutdownAsync();
                            }
                            else
                            {
                                host.RunAsService();
                            }
                        }
                        catch (TaskCanceledException)
                        {
                            return(0);
                        }
                        catch (Exception ex)
                        {
                            logger.LogError("Start failed due to an unexpected exception: {0}", ex);
                            return(1);
                        }

                        return(0);
                    }
                });
            });

            if (args.Length == 0)
            {
                app.ShowHint();
            }
            else
            {
                app.Execute(args);
            }
        }