Example #1
0
        /// <inheritdoc/>
        protected async override Task <int> OnRunAsync()
        {
            // Read the configuration environment variable or file to initialize
            // endpoint response text.

            responseText = "UNCONFIGURED";

            var resultVar = GetEnvironmentVariable("WEB_RESULT");

            if (resultVar != null)
            {
                responseText = resultVar;
            }
            else
            {
                var configPath = GetConfigFilePath("/etc/complex/response");

                if (configPath != null && File.Exists(configPath))
                {
                    responseText = File.ReadAllText(configPath);
                }
            }

            // Start the web service.

            var endpoint = Description.Endpoints.Default;

            webHost = new WebHostBuilder()
                      .UseStartup <ComplexServiceStartup>()
                      .UseKestrel(options => options.Listen(IPAddress.Any, endpoint.Port))
                      .ConfigureServices(services => services.AddSingleton(typeof(ComplexService), this))
                      .Build();

            webHost.Start();

            // Start the worker thread.

            thread = NeonHelper.StartThread(ThreadFunc);

            // Start the service task

            task = Task.Run(async() => await TaskFunc());

            // Indicate that the service is running.

            await StartedAsync();

            // Handle termination gracefully.

            await Terminator.StopEvent.WaitAsync();

            thread.Join();
            await task;

            Terminator.ReadyToExit();

            return(0);
        }
Example #2
0
        /// <inheritdoc/>
        protected async override Task <int> OnRunAsync()
        {
            await SetStatusAsync(NeonServiceStatus.Starting);

            Kubernetes = new KubernetesWithRetry(KubernetesClientConfiguration.BuildDefaultConfig());

            // Start the web service.
            var port = 443;

            if (NeonHelper.IsDevWorkstation)
            {
                port = 11004;
            }

            webHost = new WebHostBuilder()
                      .ConfigureAppConfiguration(
                (hostingcontext, config) =>
            {
                config.Sources.Clear();
            })
                      .UseStartup <Startup>()
                      .UseKestrel(options => {
                options.Listen(IPAddress.Any, port, listenOptions =>
                {
                    if (!NeonHelper.IsDevWorkstation)
                    {
                        listenOptions.UseHttps(X509Certificate2.CreateFromPem(
                                                   File.ReadAllText(@"/tls/tls.crt"),
                                                   File.ReadAllText(@"/tls/tls.key")));
                    }
                });
            })
                      .ConfigureServices(services => services.AddSingleton(typeof(Service), this))
                      .UseStaticWebAssets()
                      .Build();

            _ = webHost.RunAsync();

            Log.LogInfo($"Listening on {IPAddress.Any}:{port}");

            // Indicate that the service is ready for business.

            await SetStatusAsync(NeonServiceStatus.Running);

            // Handle termination gracefully.

            await Terminator.StopEvent.WaitAsync();

            Terminator.ReadyToExit();

            return(0);
        }
Example #3
0
        /// <inheritdoc/>
        protected async override Task <int> OnRunAsync()
        {
            await SetStatusAsync(NeonServiceStatus.Starting);

            Config = await ProxyConfig.FromFileAsync(GetConfigFilePath(ConfigFile));

            DnsClient = new LookupClient(new LookupClientOptions()
            {
                UseCache            = Config.Dns.UseCache,
                MaximumCacheTimeout = TimeSpan.FromSeconds(Config.Dns.MaximumCacheTimeoutSeconds),
                MinimumCacheTimeout = TimeSpan.FromSeconds(Config.Dns.MinimumCacheTimeoutSeconds),
                CacheFailedResults  = Config.Dns.CacheFailedResults
            });

            AesCipher = new AesCipher(GetEnvironmentVariable("COOKIE_CIPHER", AesCipher.GenerateKey(), redacted: !Log.IsLogDebugEnabled));

            CurrentConnections = new HashSet <string>();

            // Start the web service.

            webHost = new WebHostBuilder()
                      .ConfigureAppConfiguration(
                (hostingcontext, config) =>
            {
                config.Sources.Clear();
            })
                      .UseStartup <Startup>()
                      .UseKestrel(options => options.Listen(IPAddress.Any, Config.Port))
                      .ConfigureServices(services => services.AddSingleton(typeof(Service), this))
                      .UseStaticWebAssets()
                      .Build();

            _ = webHost.RunAsync();

            Log.LogInfo($"Listening on {IPAddress.Any}:{Config.Port}");

            // Indicate that the service is ready for business.

            await SetStatusAsync(NeonServiceStatus.Running);

            // Handle termination gracefully.

            await Terminator.StopEvent.WaitAsync();

            Terminator.ReadyToExit();

            return(0);
        }
Example #4
0
        /// <inheritdoc/>
        protected async override Task <int> OnRunAsync()
        {
            // Query the service map for the [web-service] endpoint and setup
            // the HTTP client we'll use to communicate with that service.

            var webService = ServiceMap["web-service"];

            if (webService == null)
            {
                Log.LogError("Service description for [web-service] not found.");
                Exit(1);
            }

            httpClient = new HttpClient()
            {
                BaseAddress = webService.Endpoints.Default.Uri
            };

            // Start the HTTP service.

            var endpoint = Description.Endpoints.Default;

            webHost = new WebHostBuilder()
                      .UseStartup <RelayServiceStartup>()
                      .UseKestrel(options => options.Listen(IPAddress.Any, endpoint.Port))
                      .ConfigureServices(services => services.AddSingleton(typeof(RelayService), this))
                      .Build();

            webHost.Start();

            // Indicate that the service is running.

            await StartedAsync();

            // Handle termination gracefully.

            await Terminator.StopEvent.WaitAsync();

            Terminator.ReadyToExit();

            return(0);
        }
Example #5
0
        /// <inheritdoc/>
        protected async override Task <int> OnRunAsync()
        {
            await SetStatusAsync(NeonServiceStatus.Starting);

            var configFile = GetConfigFilePath("/etc/neonkube/neon-sso-session-proxy/config.yaml");

            using (StreamReader reader = new StreamReader(new FileStream(configFile, FileMode.Open, FileAccess.Read)))
            {
                Config = NeonHelper.YamlDeserializeViaJson <DexConfig>(await reader.ReadToEndAsync());
            }

            // Start the web service.

            webHost = new WebHostBuilder()
                      .ConfigureAppConfiguration(
                (hostingcontext, config) =>
            {
                config.Sources.Clear();
            })
                      .UseStartup <Startup>()
                      .UseKestrel(options => options.Listen(IPAddress.Any, 80))
                      .ConfigureServices(services => services.AddSingleton(typeof(Service), this))
                      .UseStaticWebAssets()
                      .Build();

            _ = webHost.RunAsync();

            Log.LogInfo($"Listening on {IPAddress.Any}:80");

            // Indicate that the service is ready for business.

            await SetStatusAsync(NeonServiceStatus.Running);

            // Handle termination gracefully.

            await Terminator.StopEvent.WaitAsync();

            Terminator.ReadyToExit();

            return(0);
        }
Example #6
0
        /// <inheritdoc/>
        protected async override Task <int> OnRunAsync()
        {
            // Load the configuration environment variables, exiting with a
            // non-zero exit code if they don't exist.

            NatsServerUri = Environment.Get("NATS_URI", string.Empty);

            if (string.IsNullOrEmpty(NatsServerUri))
            {
                Log.LogCritical("Invalid configuration: [NATS_URI] environment variable is missing or invalid.");
                Exit(1);
            }

            // Start the HTTP service.

            var endpoint = Description.Endpoints.Default;

            webHost = new WebHostBuilder()
                      .UseStartup <Startup>()
                      .UseKestrel(options =>
            {
                options.Listen(IPAddress.Any, endpoint.Port);
            })
                      .ConfigureServices(services => services.AddSingleton(typeof(WebService), this))
                      .Build();

            webHost.Start();

            // Indicate that the service is running.

            await StartedAsync();

            // Handle termination gracefully.

            await Terminator.StopEvent.WaitAsync();

            Terminator.ReadyToExit();

            return(0);
        }
Example #7
0
        /// <inheritdoc/>
        protected async override Task <int> OnRunAsync()
        {
            // Parse the environment variables.

            var portVariable = GetEnvironmentVariable("PORT", "80");

            if (!int.TryParse(portVariable, out var port) || !NetHelper.IsValidPort(port))
            {
                Log.LogCritical($"[PORT={port}] environment variable is not valid.");
                return(1);
            }

            // Start the HTTP service.

            webHost = new WebHostBuilder()
                      .UseStartup <Startup>()
                      .UseKestrel(options => options.Listen(IPAddress.Any, port))
                      .ConfigureServices(services => services.AddSingleton(typeof(Service), this))
                      .Build();

            webHost.Start();

            // Start a do-nothing thread that we can use to set breakpoints
            // to verify that Bridge to Kubernetes works.

            var nothingThread = NeonHelper.StartThread(NothingThread);

            // Indicate that the service is running.

            await StartedAsync();

            // Handle termination gracefully.

            await Terminator.StopEvent.WaitAsync();

            nothingThread.Join();
            Terminator.ReadyToExit();

            return(0);
        }
Example #8
0
        /// <inheritdoc/>
        protected async override Task<int> OnRunAsync()
        {
            // Load the configuration environment variables, exiting with a
            // non-zero exit code if they don't exist.

            natsServerUri = Environment.Get("NATS_URI", string.Empty);

            if (string.IsNullOrEmpty(natsServerUri))
            {
                Log.LogCritical("Invalid configuration: [NATS_URI] environment variable is missing or invalid.");
                Exit(1);
            }

            natsQueue = GetEnvironmentVariable("NATS_QUEUE");

            if (string.IsNullOrEmpty(natsQueue))
            {
                Log.LogCritical("Invalid configuration: [NATS_QUEUE] environment variable is missing or invalid.");
                Exit(1);
            }

            // Connect to NATS.

            var connectionFactory = new ConnectionFactory();
            var natOptions        = ConnectionFactory.GetDefaultOptions();

            natOptions.Servers = new string[] { natsServerUri };

            nats = connectionFactory.CreateConnection(natOptions);

            // Start the service tasks

            sendTask    = Task.Run(async () => await SendTaskFunc());
            receiveTask = Task.Run(async () => await ReceiveTaskFunc());

            // Indicate that the service is running.

            await StartedAsync();

            // Handle termination gracefully.

            await Terminator.StopEvent.WaitAsync();

            // We're going to dispose the NATS connection which will cause any 
            // pending or future NAT calls to fail and throw an exception.  This
            // will ultimately cause the tasks to exit.
            //
            // Note that we're setting [terminate=true] so the task exception
            // handlers can handle termination related exceptions by not logging
            // them and exiting the task.

            terminating = true;

            nats.Dispose();

            // Wait for the service task to exit and then indicate that the 
            // service has exited cleanly.

            await sendTask;
            Terminator.ReadyToExit();

            return 0;
        }
Example #9
0
        /// <summary>
        /// Starts the service if it's not already running.  This will call <see cref="OnRunAsync"/>,
        /// which actually implements the service.
        /// </summary>
        /// <param name="disableProcessExit">
        /// Optionally specifies that the hosting process should not be terminated
        /// when the service exists.  This is typically used for testing or debugging.
        /// This defaults to <c>false</c>.
        /// </param>
        /// <remarks>
        /// <note>
        /// For production, this method will not return until the service is expicitly
        /// stopped via a call to <see cref="Stop"/> or the <see cref="Terminator"/>
        /// handles a stop signal.  For test environments, this method will call
        /// <see cref="OnRunAsync"/> on a new thread and returns immediately while the
        /// service continues to run in parallel.
        /// </note>
        /// <para>
        /// Service implementations must honor <see cref="Terminator"/> termination
        /// signals exiting the <see cref="OnRunAsync"/> method reasonably quickly (within
        /// 30 seconds by default) when these occur.  They can do this by passing
        /// <see cref="ProcessTerminator.CancellationToken"/> for <c>async</c> calls
        /// and then catching the <see cref="TaskCanceledException"/> and returning
        /// from <see cref="OnRunAsync"/>.
        /// </para>
        /// <para>
        /// Another technique for synchronous code is to explicitly check the
        /// <see cref="ProcessTerminator.CancellationToken"/> token's
        /// <see cref="CancellationToken.IsCancellationRequested"/> property and
        /// return from your <see cref="OnRunAsync"/> method when this is <c>true</c>.
        /// This You'll need to perform this check frequently so you may need
        /// to use timeouts to prevent blocking code from blocking for too long.
        /// </para>
        /// </remarks>
        /// <returns>The service exit code.</returns>
        /// <remarks>
        /// <note>
        /// It is not possible to restart a service after it's been stopped.
        /// </note>
        /// </remarks>
        public async virtual Task <int> RunAsync(bool disableProcessExit = false)
        {
            lock (syncLock)
            {
                if (isRunning)
                {
                    throw new InvalidOperationException($"Service [{Name}] is already running.");
                }

                if (isDisposed)
                {
                    throw new InvalidOperationException($"Service [{Name}] cannot be restarted after it's been stopped.");
                }

                isRunning = true;
            }

            // [disableProcessExit] will be typically passed as true when testing or
            // debugging.  We'll let the terminator know so it won't do this.

            if (disableProcessExit)
            {
                Terminator.DisableProcessExit = true;
            }

            // Initialize the logger.

            if (GlobalLogging)
            {
                LogManager = global::Neon.Diagnostics.LogManager.Default;
            }
            else
            {
                LogManager = new LogManager(parseLogLevel: false);
            }

            LogManager.SetLogLevel(GetEnvironmentVariable("LOG_LEVEL", "info"));

            Log = LogManager.GetLogger();
            Log.LogInfo(() => $"Starting [{Name}:{GitVersion}]");

            // Start and run the service.

            try
            {
                await OnRunAsync();

                ExitCode = 0;
            }
            catch (TaskCanceledException)
            {
                // Ignore these as a normal consequence of a service
                // being signalled to terminate.

                ExitCode = 0;
            }
            catch (ProgramExitException e)
            {
                // Don't override a non-zero ExitCode that was set earlier
                // with a zero exit code.

                if (e.ExitCode != 0)
                {
                    ExitCode = e.ExitCode;
                }
            }
            catch (Exception e)
            {
                ExitException = e;

                Log.LogError(e);
            }

            // Perform last rights for the service before it passes away.

            Log.LogInfo(() => $"Exiting [{Name}] with [exitcode={ExitCode}].");
            Terminator.ReadyToExit();

            Status = KubeServiceStatus.Terminated;

            return(ExitCode);
        }
Example #10
0
        /// <inheritdoc/>
        protected async override Task <int> OnRunAsync()
        {
            //-----------------------------------------------------------------
            // Start the controllers: these need to be started before starting KubeOps

            var k8s = new Kubernetes(KubernetesClientConfiguration.BuildDefaultConfig());

            //################################
            // $debug(jefflill): RESTORE THIS!
            //await NodeTaskController.StartAsync(k8s);
            //################################

            await ContainerRegistryController.StartAsync(k8s);

            //-----------------------------------------------------------------
            // Start KubeOps.

            // $hack(jefflill): https://github.com/nforgeio/neonKUBE/issues/1599
            //
            // We're temporarily using our poor man's operator

#if DISABLED
            _ = Host.CreateDefaultBuilder()
                .ConfigureHostOptions(
                options =>
            {
                // Ensure that the processor terminator and ASP.NET shutdown times match.

                options.ShutdownTimeout = ProcessTerminator.DefaultMinShutdownTime;
            })
                .ConfigureAppConfiguration(
                (hostingContext, config) =>
            {
                // $note(jefflill):
                //
                // The .NET runtime watches the entire file system for configuration
                // changes which can cause real problems on Linux.  We're working around
                // this by removing all configuration sources which we aren't using
                // anyway for Kubernetes apps.
                //
                // https://github.com/nforgeio/neonKUBE/issues/1390

                config.Sources.Clear();
            })
                .ConfigureLogging(
                logging =>
            {
                logging.ClearProviders();
                logging.AddProvider(base.LogManager);
            })
                .ConfigureWebHostDefaults(builder => builder.UseStartup <Startup>())
                .Build()
                .RunOperatorAsync(Array.Empty <string>());
#endif

            // Indicate that the service is running.

            await StartedAsync();

            // Handle termination gracefully.

            await Terminator.StopEvent.WaitAsync();

            Terminator.ReadyToExit();

            return(0);
        }
Example #11
0
        /// <inheritdoc/>
        protected async override Task <int> OnRunAsync()
        {
            // Verify the environment variables.

            var settings   = new TemporalSettings();
            var hostPort   = GetEnvironmentVariable("TEMPORAL_HOSTPORT");
            var @namespace = GetEnvironmentVariable("TEMPORAL_NAMESPACE");
            var taskQueue  = GetEnvironmentVariable("TEMPORAL_TASKQUEUE");
            var error      = false;

            Log.LogInfo(() => $"TEMPORAL_HOSTPORT:  {hostPort}");
            Log.LogInfo(() => $"TEMPORAL_NAMESPACE: {@namespace}");
            Log.LogInfo(() => $"TEMPORAL_TASKQUEUE: {taskQueue}");

            if (string.IsNullOrEmpty(hostPort))
            {
                error = true;
                Log.LogError("The [TEMPORAL_HOSTPORT] environment variable is required.");
            }

            settings.HostPort = hostPort;

            if (string.IsNullOrEmpty(@namespace))
            {
                error = true;
                Log.LogError("The [TEMPORAL_NAMESPACE] environment variable is required.");
            }

            if (string.IsNullOrEmpty(taskQueue))
            {
                error = true;
                Log.LogError("The [TEMPORAL_TASKQUEUE] environment variable is required.");
            }

            if (error)
            {
                return(1);
            }

            // Connect to Temporal and register the workflows and activities.

            Log.LogInfo("Connecting to Temporal.");

            settings.Namespace = @namespace;
            settings.TaskQueue = taskQueue;

            using (var client = await TemporalClient.ConnectAsync(settings))
            {
                // Create a worker and register the workflow and activity
                // implementations to let Temporal know we're open for business.

                using (var worker = await client.NewWorkerAsync())
                {
                    Log.LogInfo("Registering workflows.");
                    await worker.RegisterAssemblyAsync(Assembly.GetExecutingAssembly());

                    Log.LogInfo("Starting worker.");
                    await worker.StartAsync();

                    // Indicate that the service is running.

                    Log.LogInfo("Ready for work.");
                    await StartedAsync();
                }
            }

            // Handle termination gracefully.

            await Terminator.StopEvent.WaitAsync();

            Terminator.ReadyToExit();

            return(0);
        }
Example #12
0
        /// <inheritdoc/>
        protected async override Task <int> OnRunAsync()
        {
            await SetStatusAsync(NeonServiceStatus.Starting);

            var port = 80;

            Kubernetes = new KubernetesWithRetry(KubernetesClientConfiguration.BuildDefaultConfig());

            _ = Kubernetes.WatchAsync <V1ConfigMap>(async(@event) =>
            {
                await SyncContext.Clear;

                ClusterInfo = TypeSafeConfigMap <ClusterInfo> .From(@event.Value).Config;
                Log.LogInfo($"Updated cluster info");
            },
                                                    KubeNamespace.NeonStatus,
                                                    fieldSelector: $"metadata.name={KubeConfigMapName.ClusterInfo}");

            Dashboards = new List <Dashboard>();
            Dashboards.Add(
                new Dashboard(
                    id:           "neonkube",
                    name:         "neonKUBE",
                    displayOrder: 0));

            _ = Kubernetes.WatchAsync <V1NeonDashboard>(async(@event) =>
            {
                await SyncContext.Clear;

                switch (@event.Type)
                {
                case WatchEventType.Added:

                    await AddDashboardAsync(@event.Value);
                    break;

                case WatchEventType.Deleted:

                    await RemoveDashboardAsync(@event.Value);
                    break;

                case WatchEventType.Modified:

                    await RemoveDashboardAsync(@event.Value);
                    await AddDashboardAsync(@event.Value);
                    break;

                default:

                    break;
                }

                Dashboards = Dashboards.OrderBy(d => d.DisplayOrder)
                             .ThenBy(d => d.Name).ToList();
            });

            if (NeonHelper.IsDevWorkstation)
            {
                port = 11001;
                SetEnvironmentVariable("LOG_LEVEL", "debug");
                SetEnvironmentVariable("DO_NOT_TRACK", "true");
                SetEnvironmentVariable("COOKIE_CIPHER", "/HwPfpfACC70Rh1DeiMdubHINQHRGfc4JP6DYcSkAQ8=");
                await ConfigureDevAsync();
            }

            var metricsHost = GetEnvironmentVariable("METRICS_HOST", "http://mimir-query-frontend.neon-monitor.svc.cluster.local:8080");

            PrometheusClient = new PrometheusClient($"{metricsHost}/prometheus/");

            SsoClientSecret = GetEnvironmentVariable("SSO_CLIENT_SECRET", redacted: !Log.IsLogDebugEnabled);

            AesCipher = new AesCipher(GetEnvironmentVariable("COOKIE_CIPHER", AesCipher.GenerateKey(), redacted: !Log.IsLogDebugEnabled));

            // Start the web service.

            webHost = new WebHostBuilder()
                      .ConfigureAppConfiguration(
                (hostingcontext, config) =>
            {
                config.Sources.Clear();
            })
                      .UseStartup <Startup>()
                      .UseKestrel(options => options.Listen(IPAddress.Any, port))
                      .ConfigureServices(services => services.AddSingleton(typeof(Service), this))
                      .UseStaticWebAssets()
                      .Build();

            _ = webHost.RunAsync();

            Log.LogInfo($"Listening on {IPAddress.Any}:{port}");

            // Indicate that the service is running.

            await StartedAsync();

            // Handle termination gracefully.

            await Terminator.StopEvent.WaitAsync();

            Terminator.ReadyToExit();

            return(0);
        }
Example #13
0
        /// <inheritdoc/>
        protected async override Task <int> OnRunAsync()
        {
            // Verify the environment variables.

            var settings = new CadenceSettings();
            var servers  = GetEnvironmentVariable("CADENCE_SERVERS");
            var domain   = GetEnvironmentVariable("CADENCE_DOMAIN");
            var taskList = GetEnvironmentVariable("CADENCE_TASKLIST");
            var error    = false;

            Log.LogInfo(() => $"CADENCE_SERVERS:  {servers}");
            Log.LogInfo(() => $"CADENCE_DOMAIN:   {domain}");
            Log.LogInfo(() => $"CADENCE_TASKLIST: {taskList}");

            if (string.IsNullOrEmpty(servers))
            {
                error = true;
                Log.LogError("The [CADENCE_SERVERS] environment variable is required.");
            }

            try
            {
                foreach (var item in servers.Split(','))
                {
                    var uri = new Uri(item.Trim(), UriKind.Absolute);

                    settings.Servers.Add(uri.ToString());
                }
            }
            catch
            {
                error = true;
                Log.LogError(() => $"One or more URIs are invalid: CADENCE_SERVERS={servers}");
            }

            if (string.IsNullOrEmpty(domain))
            {
                error = true;
                Log.LogError("The [CADENCE_DOMAIN] environment variable is required.");
            }

            if (string.IsNullOrEmpty(taskList))
            {
                error = true;
                Log.LogError("The [CADENCE_TASKLIST] environment variable is required.");
            }

            if (error)
            {
                return(1);
            }

            // Connect to Cadence and register the workflows and activities.

            Log.LogInfo("Connecting to Cadence.");

            settings.DefaultDomain = domain;

            using (var client = await CadenceClient.ConnectAsync(settings))
            {
                // Register the workflows.

                Log.LogInfo("Registering workflows.");
                await client.RegisterAssemblyAsync(Assembly.GetExecutingAssembly());

                // Start the worker.

                Log.LogInfo("Starting worker.");

                using (var worker = client.StartWorkerAsync(taskList))
                {
                    // Indicate that the service is running.

                    Log.LogInfo("Ready for work.");
                    await StartedAsync();
                }
            }

            // Handle termination gracefully.

            await Terminator.StopEvent.WaitAsync();

            Terminator.ReadyToExit();

            return(0);
        }