Class NeonService
Handy base class for application services. This class handles process termination signals when running on Linux, OS/X, and similar environments and also provides some features to help you run unit tests on your service.
Namespace: Neon.Service
Assembly: Neon.Service.dll
Syntax
public abstract class NeonService
Remarks
Basing your service implementations on the Neon.Service class will make them easier to test via integration with the ServiceFixture from the Neon.Xunit library by providing some useful abstractions over service configuration, startup and shutdown including a ProcessTerminator to handle termination signals from Linux or Kubernetes.
This class is pretty easy to use. Simply derive your service class from NeonService and implement the OnRunAsync() method. OnRunAsync() will be called when your service is started. This is where you'll implement your service. You should perform any initialization and then call StartedAsync(NeonServiceStatus) to indicate that the service is ready for business.
note
We recommend that your service constructor be limited to configuring base service properties and that you perform the bulk of your service initialization in OnRunAsync() before you call StartedAsync(NeonServiceStatus). Any logging performed in the constructor will be handled by a default console logger because the regular logger isn't initialized until RunAsync(bool) is called to start the service. We recommend that you avoid any logging from within your constructor.
note
Note that calling StartedAsync(NeonServiceStatus) after your service has initialized is important because the NeonServiceFixture won't allow tests to proceed until the service indicates that it's ready. This is necessary to avoid unit test race conditions.
Note that your OnRunAsync() method should generally not return until the Terminator signals it to stop. Alternatively, you can throw a ProgramExitException with an optional process exit code to proactively exit your service.
note
All services should properly handle Terminator stop signals so services deployed as containers will stop promptly and cleanly (this also applies to services running in unit tests). Your terminate handler method must return within a set period of time (30 seconds by default) to avoid killed by by Docker or Kubernetes. This is probably the trickiest thing you'll need to implement. For asynchronous service implementations, you consider passing the CancellationToken to all async method calls.
note
This class uses the DEV_WORKSTATION environment variable to determine whether the service is running in test mode or not. This variable will typically be defined on developer workstations as well as CI/CD machines. This variable must never be defined for production environments. You can use the InProduction or InDevelopment properties to check this.
CONFIGURATION
Services are generally configured using environment variables and/or configuration files. In production, environment variables will actually come from the environment after having been initialized by the container image or passed by Kubernetes when starting the service container. Environment variables are retrieved by case sensitive name.
Configuration files work the same way. They are either present in the service container image or mounted to the container as a secret or config file by Kubernetes. Configuration files are specified by their path (case sensitive) within the running container.
This class provides some abstractions for managing environment variables and configuration files so that services running in production or as a unit test can configure themselves using the same code for both environments.
Services should use the Environment parser methods to retrieve important variables rather obtaining these via GetEnvironmentVariable(string). These methods handle type-safe parsing, validation and default values.
In production, this simply returns values from the process environment variables. For tests, the environment variable can be returned from a local dictionary that was expicitly initialized by calls to SetEnvironmentVariable(string, string). This local dictionary allows the testing of multiple services at the same time with each being presented their own environment variables.
You may also use the LoadEnvironmentVariableFile(string, Func<string, string>) methods to load environment variables from a text file (potentially encrypted via NeonVault). This will typically be done only for unit tests.
Configuration files work similarily. You'll use GetConfigFilePath(string)
to map a logical file path to a physical path. The logical file path is typically
specified as the path where the configuration file will be located in production.
This can be any valid path with in a running production container and since we're
currently Linux centric, will typically be a Linux file path like /etc/MYSERVICE.yaml
or /etc/MYSERVICE/config.yaml
.
For production, GetConfigFilePath(string) will simply return the file path passed so that the configuration file located there will referenced. For testing, GetConfigFilePath(string) will return the path specified by an earlier call to SetConfigFilePath(string, string, Func<string, string>) or to a temporary file initialized by previous calls to SetConfigFile(string, string, bool) or SetConfigFile(string, byte[]). This indirection provides a consistent way to run services in production as well as in tests, including tests running multiple services simultaneously.
DISPOSE IMPLEMENTATION
All services, especially those that create unmanaged resources like ASP.NET services, sockets, NATS clients, HTTP clients, thread etc. should override and implement Dispose(bool) to ensure that any of these resources are proactively disposed. Your method should call the base class version of the method first before disposing these resources.
protected override Dispose(bool disposing)
{
base.Dispose(disposing);
if (appHost != null)
{
appHost.Dispose();
appHost = null;
}
}
The disposing parameter is passed as true
when the base Dispose()
method was called or false
if the garbage collector is finalizing the instance
before discarding it. The difference is subtle and most services can safely ignore
this parameter (other than passing it through to the base Dispose(bool)
method).
In the example above, the service implements an ASP.NET web service where appHost
was initialized as the IWebHost
actually implementing the web service. The code
ensures that the appHost
isn't already disposed before disposing it. This will
stop the web service and release the underlying listening socket. You'll want to do
something like this for any other unmanaged resources your service might hold.
note
It's very important that you take care to dispose things like running web services and listening sockets within your Dispose(bool) method. You also need to ensure that any threads you've created are terminated. This means that you'll need a way to signal threads to exit and then wait for them to actually exit.
This is important when testing your services with a unit testing framework like Xunit because frameworks like this run all tests within the same Test Runner process and leaving something like a listening socket open on a port (say port 80) may prevent a subsequent test from running successfully due to it not being able to open its listening socket on port 80.
LOGGING
The NeonService constructor initializes the OpenTelemetry logging pipeline by default, initializing the Logger property as well as initializing LoggerFactory and adding the logger factor and logger to ServiceContainer, making them available to NEONSDK libraries so they may emit logs. The logger created by NeonService in this case will be configured to write JSON formatted logs to the process standard output stream.
NeonService parses the current log level from the LOG_LEVEL environment variable, defaulting to Information when this variable is present or cannot be parsed. The possible log levels are: CRITICAL, ERROR, WARNING, WARN, INFORMATION, INFO, DEBUG or TRACE (case insensitive).
If you need to customize the OpenTelemetry logging pipeline, you may accomplish this two ways:
-
The easist way to tweak the logging configuration is to implement the protected OnLoggerConfg(OpenTelemetryLoggerOptions) method. This is called by the NeonService constructor before any built-in logging configuration has been done, giving your code the chance to modify the options.
Your OnLoggerConfg(OpenTelemetryLoggerOptions) method can then return
false
when you want NeonService to continue with it's built-in configuration ortrue
if the service ill use your configuration unchanged. -
You can configuring the pipeline before intantiating your service, by creating an ILoggerFactory, doing any configuration as desired, setting the LoggerFactory to this factory and then passing the options to the NeonService constructor.
OnLoggerConfg(OpenTelemetryLoggerOptions) isn't called when a LoggerFactory instance is passed and the service just use your configuration.
Your service can use the base NeonService;s Logger property or
call CreateLogger<T>(LogAttributes, bool, bool) or
CreateLogger(string, LogAttributes, bool, bool) to create loggers
you can use for logging events. We then recommend that you add a using Neon.Diagnostics;
statement to your code and then use our LoggerExtensions like:
using System;
using System.Diagnostics;
...
var logger = TelemetryHub.CreateLogger("my-logger");
var username = "Sally";
logger.LogDebugEx("this is a test");
logger.LogInformationEx(() => $"user: {username}");
TRACING
NeonService also has limited support for configuring OpenTelemetry tracing for targeting an OpenTelemetry Collector by URI. This is controlled using two environment variables:
note
NeonService only supports exporting traces to an OpenTelemetry Collector via and OTEL exporter, but you can always configure a custom OpenTelemetry pipeline before starting your NeonService when you need to send traces elsewhere.
TRACE_COLLECTOR_URI |
When present and TracerProvider is note
This is ignored when TracerProvider is not |
TRACE_LOG_LEVEL |
This optional environment variable configures the log level for events that will be also recorded as trace events. You can also specify TRACE_LOG_LEVEL=None to disable automatic trace event submission completely.
This defaults to the noteThis feature is disabled when LoggerFactory has been set, indicating that the OpenTelemetry logging pipeline has already been customized. note
Only events logged at the current LOG_LEVEL or greater will ever be candidates for being recorded as trace events; TRACE_LOG_LEVEL is intended to be used for adding an additional constrain on trace events over and above the LOG_LEVEL.
For example, by setting
Setting |
HEALTH PROBES
note
Health probes are supported only on Linux running as AMD64. This is not supported on Windows, OS/X, 32-bit or ARM platforms at this time.
Hosting environments such as Kubernetes will often require service instances to be able to report their health via health probes. These probes are typically implemented as a small executables that is called periodically by the hosting environment with the return code indicating the service instance health. Alternatively, services can expose one or more web endpoints that returns 200 status when the service is healthy.
note
This class has built-in support for the the small executable health checker approach to make it easier to implement health checks for workloads that don't expose a web interface. Web workloads can continue use the built-in approach or just expose their own health endpoints.
The NeonService class supports this by optionally writing a text file with various strings indicating the health status. The status file will consist of a single line of text holding one of the serialized NeonServiceStatus values. The status file path defaults to /health-status.
NeonService also deploys two status checking tools called health-check and ready-check to the same directory where health-status is written.
The health check tool will be created at /health-check by default and it returns a non-zero exit code when the service is not healthy. A service is considered healthy only when the status is on of Running or NotReady.
The ready check tool file will be created at /ready-check by default and it returns a non-zero exit code when the service is not ready. A service is considered ready only when the status is Running.
You may pass a custom health folder path to the constructor so that the status and check files so these can be located elsewhere to avoid conflicts such as when multiple services will be running on a machine or container or when the root file system is read-only. You can also disable this feature entirely by passing "DISABLED" as the health folder path.
note
For Kubernetes deployments, we recommend that you configure your pod specifications with startup and liveliness probes along with an optional readiness probe when appropriate. This will look something like:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
selector:
matchLabels:
operator: my-app
template:
metadata:
labels:
operator: my-app
spec:
enableServiceLinks: false
containers:
- name: my-app
image: docker.io/my-app:latest
startupProbe:
exec:
command:
- /health-check # $lt;--- this script works for both startup and liveliness probes
initialDelaySeconds: 1
periodSeconds: 5
timeoutSeconds: 1
livenessProbe:
exec:
command:
- /health-check
initialDelaySeconds: 1 # $lt;--- we don't need a long (fixed) delay here with a startup probe
periodSeconds: 5
timeoutSeconds: 1
readinessProbe:
exec:
command:
- /ready-check # $lt;--- separate script for readiness probes
initialDelaySeconds: 1
periodSeconds: 5
timeoutSeconds: 1
ports:
- containerPort: 5000
name: http
terminationGracePeriodSeconds: 10
SERVICE DEPENDENCIES
Services often depend on other services to function, such as databases, REST APIs, etc. NeonService provides an easy to use integrated way to wait for other services to initialize themselves and become ready before your service will be allowed to start. This is a great way to avoid a blizzard of service failures and restarts when starting a collection of related services on a platform like Kubernetes.
You can use the Dependencies property to control this in code via the ServiceDependencies class or configure this via environment variables:
NEON_SERVICE_DEPENDENCIES_URIS=http://foo.com;tcp://10.0.0.55:1234
NEON_SERVICE_DEPENDENCIES_TIMEOUT_SECONDS=30
NEON_SERVICE_DEPENDENCIES_WAIT_SECONDS=5
The basic idea is that the RunAsync(bool) call to start your service will need to successfully to establish socket connections to any service dependecy URIs before your OnRunAsync() method will be called. Your service will be terminated if any of the services cannot be reached after the specified timeout.
You can also specify additional time to wait after all services are available to give them a chance to perform additional internal initialization.
note
Service dependencies are currently waited for when the service status is Starting, which means that they will need to complete before the startup or libeliness probes time out resulting in service termination. This behavior may change in the future.
PROMETHEUS METRICS
NeonService can enable services to publish Prometheus metrics with a single line of code; simply set MetricsOptions.Mode to Scrape before calling RunAsync(bool). This configures your service to publish metrics via HTTP via http://0.0.0.0:PrometheusMetrics/metrics/. We've resistered port PrometheusMetrics with Prometheus as a standard port to be used for microservices running in Kubernetes or on other container platforms to make it easy configure scraping for a cluster.
You can also configure a custom port and path or configure metrics push to a Prometheus Pushgateway using other MetricsOptions properties. You can also fully customize your Prometheus configuration by leaving this disabled in MetricsOptions and setting things up using the standard prometheus-net mechanisms before calling RunAsync(bool).
.NET CORE RUNTIME METRICS
For .NET Core apps, you'll also need to add a reference to the prometheus-net.DotNetRuntime nuget package and set GetCollector to a function that returns the the collector to be used. This will look like:
Service.MetricsOptions.GetCollector =
() =>
{
return DotNetRuntimeStatsBuilder
.Default()
.StartCollecting();
};
ASPNETCORE METRICS:
For ASPNETCORE metrics, you'll need to add the prometheus-net.AspNetCore nuget package and then add the collector when configuring your application in the Startup class, like:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseHttpMetrics(); // <--- add this
}
CRON JOBS
NeonServices that implement Kubernetes CRON jobs should consider setting
AutoTerminateIstioSidecar=true
. This ensures that the pod scheduled
for the job is terminated cleanly when it has Istio injected sidecars. This is generally
safe to set when running in a Kubernetes cluster.
Constructors
NeonService(string, string, NeonServiceOptions)
Constructor.
Declaration
public NeonService(string name, string version = null, NeonServiceOptions options = null)
Parameters
Type | Name | Description |
---|---|---|
string | name | The name of this service. |
string | version | Optionally specifies the version of your service formatted as a valid SemanticVersion. This will default to "unknown" when not set or when the value passed is invalid. |
NeonServiceOptions | options | Optionally specifies NeonService options. |
Exceptions
Type | Condition |
---|---|
KeyNotFoundException | Thrown if there is no service description for |
Properties
ActivitySource
Configured as the activity source used by the service for recording traces.
note
NeonService also sets the same value as ActivitySource to enable tracing from library code.
Declaration
public ActivitySource ActivitySource { get; set; }
Property Value
Type | Description |
---|---|
ActivitySource |
Arguments
Returns the list of command line arguments passed to the service. This defaults to an empty list.
Declaration
public List<string> Arguments { get; }
Property Value
Type | Description |
---|---|
List<string> |
AutoTerminateIstioSidecar
Controls whether any Istio sidecars (Envoy) will be terminated automatically
when the service exits normally. This is useful for services like CRON jobs
that don't get rescheduled after they exit. This defaults to false
.
Declaration
public bool AutoTerminateIstioSidecar { get; set; }
Property Value
Type | Description |
---|---|
bool |
Remarks
You should consider setting this property to true
for services implementing
a Kubernetes CRON job that has injected Istio sidecars. The problem here is that
after the main pod container exits, the Envoy sidecar containers reamin running and
the pod never gets terminated:
https://github.com/istio/istio/issues/11659#issuecomment-809372980
This isn't an general issue for other deployments because the Kubernetes scheduler will terminate and reschedule pods after the main container exists. CRON jobs are different because they won't get rescheduled when the main container exits.
When you set AutoTerminateIstioSidecartrue
, the NeonService
class will POST a request to the Envoy sidecar's admin API at http://localhost:15000/quitquitquit
to explicitly terminate any sidecars when the service exits normally. Note that we won't
post this request when the service receives a termination signal.
We recommend that you set this property before calling StartedAsync(NeonServiceStatus) in your service initialization code.
note
This class generally tolerates the situation where the service does not have injected Istio sidecars by ignoring any network errors when posting to the Envoy admin endpoint.
BaseUri
For services with exactly one network endpoint, this returns the base URI to be used to access the service.
note
This will throw a InvalidOperationException if the service defines no endpoints or has multiple endpoints.
Declaration
public Uri BaseUri { get; }
Property Value
Type | Description |
---|---|
Uri |
Exceptions
Type | Condition |
---|---|
InvalidOperationException | Thrown when the service does not define exactly one endpoint or Description is not set. |
DebugMode
Returns true
when the service is running in debug mode.
Declaration
public bool DebugMode { get; }
Property Value
Type | Description |
---|---|
bool |
Remarks
Services use the presence of the DEV_WORKSTATION environment variable or a DEBUG environment set to one of DEBUG, true, yes, on, or 1.
If the DEBUG environment variable isn't present then debug mode will be set when DEV_WORKSTATION exists, otherwise the DEBUG variable value determines the mode.
Dependencies
Used to specify other services that must be reachable via the network before a NeonService will be allowed to start. This is exposed via the Dependencies where these values can be configured in code before RunAsync(bool) is called or they can also be configured via environment variables as described in ServiceDependencies.
note
Service dependencies are currently waited for when the service status is Starting, which means that they will need to complete before the startup or liveliness probes time out resulting in service termination. This behavior may change in the future.
Declaration
public ServiceDependencies Dependencies { get; set; }
Property Value
Type | Description |
---|---|
ServiceDependencies |
Description
Returns the service description for this service (if any).
Declaration
public ServiceDescription Description { get; }
Property Value
Type | Description |
---|---|
ServiceDescription |
Endpoints
Returns the dictionary mapping case sensitive service endpoint names to endpoint information.
Declaration
public Dictionary<string, ServiceEndpoint> Endpoints { get; }
Property Value
Type | Description |
---|---|
Dictionary<string, ServiceEndpoint> |
Environment
Provides support for retrieving environment variables as well as parsing common value types as well as custom value parsers. We recommend that services use this rather than GetEnvironmentVariable(string, string, bool) when possible as a way to standardize on how settings are formatted, parsed and validated.
Declaration
public EnvironmentParser Environment { get; }
Property Value
Type | Description |
---|---|
EnvironmentParser |
ExitCode
Returns the exit code returned by the service.
Declaration
public int ExitCode { get; }
Property Value
Type | Description |
---|---|
int |
ExitException
Returns any abnormal exception thrown by the derived OnRunAsync() method.
Declaration
public Exception ExitException { get; }
Property Value
Type | Description |
---|---|
Exception |
InDevelopment
Returns true
when the service is running in development
or test mode, when the DEV_WORKSTATION environment variable
is defined.
Declaration
public bool InDevelopment { get; }
Property Value
Type | Description |
---|---|
bool |
InProduction
Returns true
when the service is running in production,
when the DEV_WORKSTATION environment variable is
not defined. The NeonServiceFixure
will set this
to true
explicitly as well.
Declaration
public bool InProduction { get; }
Property Value
Type | Description |
---|---|
bool |
Logger
Configured as the ILogger used by the service for logging.
note
You can create additional loggers via CreateLogger(string, LogAttributes, bool, bool) and CreateLogger<T>(LogAttributes, bool, bool).
Declaration
public ILogger Logger { get; set; }
Property Value
Type | Description |
---|---|
ILogger |
LoggerFactory
Returns the ILoggerFactory used by the service. This is used to create the default service Logger and may also be used by user code to create custom loggers when necessary.
Declaration
public ILoggerFactory LoggerFactory { get; }
Property Value
Type | Description |
---|---|
ILoggerFactory |
MetricsOptions
Prometheus metrics options. To enable metrics collection for non-ASPNET applications,
we recommend that you simply set Mode==
Scrape
before calling OnRunAsync().
See MetricsOptions for more details.
Declaration
public MetricsOptions MetricsOptions { get; set; }
Property Value
Type | Description |
---|---|
MetricsOptions |
Name
Returns the service name.
Declaration
public string Name { get; }
Property Value
Type | Description |
---|---|
string |
ServiceMap
Returns the service map (if any).
Declaration
public ServiceMap ServiceMap { get; }
Property Value
Type | Description |
---|---|
ServiceMap |
Status
Returns the service current running status. Use SetStatusAsync(NeonServiceStatus) to update the service status.
Declaration
public NeonServiceStatus Status { get; }
Property Value
Type | Description |
---|---|
NeonServiceStatus |
Terminator
Returns the service's ProcessTerminator. This can be used to handle termination signals.
Declaration
public ProcessTerminator Terminator { get; }
Property Value
Type | Description |
---|---|
ProcessTerminator |
Version
Returns the service version or "unknown".
Declaration
public string Version { get; }
Property Value
Type | Description |
---|---|
string |
Methods
ClearEnvironmentVariables()
Clears any loaded environment variables.
Declaration
public void ClearEnvironmentVariables()
Dispose()
Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
Declaration
public void Dispose()
Dispose(bool)
Releases all associated resources.
Declaration
protected virtual void Dispose(bool disposing)
Parameters
Type | Name | Description |
---|---|---|
bool | disposing | Pass |
Exit(int)
Used by services to stop themselves, specifying an optional process exit code.
Declaration
public virtual void Exit(int exitCode = 0)
Parameters
Type | Name | Description |
---|---|---|
int | exitCode | The optional exit code (defaults to 0). |
Remarks
This works by setting ExitCode if exitCode
is non-zero,
signalling process termination on another thread and then throwing a ProgramExitException
on the current thread. This will generally cause the current thread or task to terminate
immediately and any other properly implemented threads and tasks to terminate gracefully
when they receive the termination signal.
~NeonService()
Finalizer.
Declaration
protected ~NeonService()
GetConfigFilePath(string)
Returns the physical path for the confguration file whose logical path is specified.
Declaration
public string GetConfigFilePath(string logicalPath)
Parameters
Type | Name | Description |
---|---|---|
string | logicalPath | The logical file path (typically expressed as a Linux path). |
Returns
Type | Description |
---|---|
string | The physical path for the configuration file or |
Remarks
note
This method does not verify that the physical file actually exists.
GetEnvironmentVariable(string, string, bool)
Returns the value of an environment variable.
Declaration
public string GetEnvironmentVariable(string name, string def = null, bool redact = false)
Parameters
Type | Name | Description |
---|---|---|
string | name | The environment variable name (case sensitive). |
string | def | The value to be returned when the environment variable doesn't exist (defaults to |
bool | redact | Optionally redact log output of the variable. |
Returns
Type | Description |
---|---|
string | The variable value or |
GetEnvironmentVariables()
Returns all loaded enviroment variables.
Declaration
public Dictionary<string, string> GetEnvironmentVariables()
Returns
Type | Description |
---|---|
Dictionary<string, string> | A dctionary mapping variable names to their values. |
LoadEnvironmentVariableFile(string, Func<string, string>)
Loads environment variables formatted as NAME=VALUE
from a text file as service
environment variables. The file will be decrypted using NeonVault if necessary.
note
Blank lines and lines beginning with '#' will be ignored.
Declaration
public void LoadEnvironmentVariableFile(string path, Func<string, string> passwordProvider = null)
Parameters
Type | Name | Description |
---|---|---|
string | path | The input file path. |
Func<string, string> | passwordProvider | Optionally specifies the password provider function to be used to locate the
password required to decrypt the source file when necessary. The password will
use a default password provider |
Remarks
The default password provider assumes that you have NEONDESKTOP installed and may be specifying passwords in the ~/.neonkube/passwords folder (relative to the current user's home directory). This will be harmless if you don't have NEONDESKTOP installed; it just probably won't find any passwords.
Implement a custom password provider function if you need something different.
Exceptions
Type | Condition |
---|---|
FileNotFoundException | Thrown if the file doesn't exist. |
FormatException | Thrown for file formatting problems. |
OnLoggerConfg(OpenTelemetryLoggerOptions)
Called by the constructor, giving the derived service implementation a chance to
customize the logger configuration using the options
parameter.
This is a good place add log exporters and processors required by your service.
This is called before the base NeonService class performs its default configuration.
Declaration
protected virtual bool OnLoggerConfg(OpenTelemetryLoggerOptions options)
Parameters
Type | Name | Description |
---|---|---|
OpenTelemetryLoggerOptions | options | Passed as the logger options. |
Returns
Type | Description |
---|---|
bool |
|
Remarks
note
This method is not called when the LoggerFactory property is set when constructing the service, indicating that the logging pipeline has already been configured.
OnRunAsync()
Called to actually implement the service.
Declaration
protected abstract Task<int> OnRunAsync()
Returns
Type | Description |
---|---|
Task<int> | The the progam exit code. |
Remarks
Services should perform any required initialization and then they must call StartedAsync(NeonServiceStatus) to indicate that the service should transition into the Running state. This is very important because the service test fixture requires the service to be in the running state before it allows tests to proceed. This is necessary to avoid unit test race conditions.
This method should return the program exit code or throw a ProgramExitException to exit with the program exit code.
OnTracerConfig(TracerProviderBuilder)
Called by the constructor, giving the derived service implementation a chance to
customize the tracer builder using the builder
parameter.
This is a good place to add OpenTelemetry.Instrumentation.AspNetCore
and other trace instrumentation required by your service.
This is called before the base NeonService class performs its default configuration.
Declaration
protected virtual bool OnTracerConfig(TracerProviderBuilder builder)
Parameters
Type | Name | Description |
---|---|---|
TracerProviderBuilder | builder | Passed as the tracer builder. |
Returns
Type | Description |
---|---|
bool |
|
Remarks
note
This method is not called when the TracerProvider property is set when constructing the service, indicating that the tracing pipeline has already been configured.
RunAsync(bool)
Starts the service if it's not already running. This will call OnRunAsync(), which is where you'll actually implement the service. Note that any service dependencies specified by Dependencies will be verified as ready before OnRunAsync() will be called.
Declaration
public virtual Task<int> RunAsync(bool disableProcessExit = false)
Parameters
Type | Name | Description |
---|---|---|
bool | 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 |
Returns
Type | Description |
---|---|
Task<int> | The service exit code. |
Remarks
note
For production, this method will not return until the service is expicitly stopped via a call to Stop() or the Terminator handles a stop signal. For test environments, this method will call OnRunAsync() on a new thread and returns immediately while the service continues to run in parallel.
Service implementations must honor Terminator termination
signals by exiting the OnRunAsync() method reasonably quickly (within
30 seconds by default) when these occur. They can do this by passing
CancellationToken for async
calls
and then catching the TaskCanceledException and returning
from OnRunAsync().
Another technique for synchronous code is to explicitly check the
CancellationToken token's
IsCancellationRequested property and
return from your OnRunAsync() method when this is true
.
You'll need to perform this check frequently so you may need
to use timeouts to prevent blocking code from blocking for too long.
SetArguments(IEnumerable<string>)
Initializes Arguments with the command line arguments passed.
Declaration
public NeonService SetArguments(IEnumerable<string> args)
Parameters
Type | Name | Description |
---|---|---|
IEnumerable<string> | args | The arguments. |
Returns
Type | Description |
---|---|
NeonService | The service instance so developers can chain fluent style calls. |
SetConfigFile(string, byte[])
Maps a logical configuration file path to a temporary file holding the byte contents passed. This is typically used initializing confguration files for unit testing.
Declaration
public NeonService SetConfigFile(string logicalPath, byte[] contents)
Parameters
Type | Name | Description |
---|---|---|
string | logicalPath | The logical file path (typically expressed as a Linux path). |
byte[] | contents | The content bytes. |
Returns
Type | Description |
---|---|
NeonService | The service instance so developers can chain fluent style calls. |
SetConfigFile(string, string, bool)
Maps a logical configuration file path to a temporary file holding the string contents passed encoded as UTF-8. This is typically used for initializing confguration files for unit testing.
Declaration
public NeonService SetConfigFile(string logicalPath, string contents, bool linuxLineEndings = false)
Parameters
Type | Name | Description |
---|---|---|
string | logicalPath | The logical file path (typically expressed as a Linux path). |
string | contents | The content string. |
bool | linuxLineEndings | Optionally convert any Windows style line endings (CRLF) into Linux
style endings (LF). This defaults to |
Returns
Type | Description |
---|---|
NeonService | The service instance so developers can chain fluent style calls. |
SetConfigFilePath(string, string, Func<string, string>)
Maps a logical configuration file path to an actual file on the local machine. This is used for unit testing to map a file on the local workstation to the path where the service expects the find to be.
Declaration
public NeonService SetConfigFilePath(string logicalPath, string physicalPath, Func<string, string> passwordProvider = null)
Parameters
Type | Name | Description |
---|---|---|
string | logicalPath | The logical file path (typically expressed as a Linux path). |
string | physicalPath | The physical path to the file on the local workstation. |
Func<string, string> | passwordProvider | Optionally specifies the password provider function to be used to locate the
password required to decrypt the source file when necessary. The password will
use a default password provider |
Returns
Type | Description |
---|---|
NeonService | The service instance so developers can chain fluent style calls. |
Remarks
The default password provider assumes that you have NEONDESKTOP installed and may be specifying passwords in the ~/.neonkube/passwords folder (relative to the current user's home directory). This will be harmless if you don't have NEONDESKTOP installed; it just probably won't find any passwords.
Implement a custom password provider function if you need something different.
Exceptions
Type | Condition |
---|---|
FileNotFoundException | Thrown if there's no file at |
SetEnvironmentVariable(string, string)
Sets or deletes a service environment variable.
Declaration
public NeonService SetEnvironmentVariable(string name, string value)
Parameters
Type | Name | Description |
---|---|---|
string | name | The variable name (case sensitive). |
string | value | The variable value or |
Returns
Type | Description |
---|---|
NeonService | The service instance so developers can chain fluent style calls. |
Remarks
note
Environment variable names are to be considered to be case sensitive since this is how Linux treats them and it's very common to be deploying services to Linux.
SetStatusAsync(NeonServiceStatus)
Updates the service status. This is typically called internally by this class but service code may set this to Unhealthy when there's a problem and back to Running when the service is healthy again. This may also be set to NotReady to indicate that the service is running but is not ready to accept external traffic.
Declaration
public Task SetStatusAsync(NeonServiceStatus newStatus)
Parameters
Type | Name | Description |
---|---|---|
NeonServiceStatus | newStatus | The new status. |
Returns
Type | Description |
---|---|
Task |
StartedAsync(NeonServiceStatus)
Called by your OnRunAsync() implementation to indicate that the service is either Running (the default) or NotReady.
Declaration
public Task StartedAsync(NeonServiceStatus status = NeonServiceStatus.Running)
Parameters
Type | Name | Description |
---|---|---|
NeonServiceStatus | status |
Returns
Type | Description |
---|---|
Task |
Remarks
For most situations, the default Running argument is appropriate. This indicates that the service will satisfy all of the probes: startup, liveliness, and readiness.
Advanced services that may take some time to perform additional initialization before being ready to service requests may pass NotReady. This means that the startup and liveliness probes will pass, preventing Kubernetes from terminating the container but that the readiness probe will fail, preventing Kubernetes from forwarding traffic to the container until Running is passed to SetStatusAsync(NeonServiceStatus).
Stop()
Stops the service if it's not already stopped. This is intended to be called by external things like unit test fixtures and is not intended to be called by the service itself.
Declaration
public virtual void Stop()
Remarks
note
It is not possible to restart a service after it's been stopped.
This is intended for internal use or managing unit test execution and is not intended for use by the service to stop itself.
Exceptions
Type | Condition |
---|---|
TimeoutException | Thrown if the service did not exit gracefully in time before it would have been killed (e.g. by Kubernetes or Docker). |
WaitUntilStarted()
Waits until the service indicate that it's started when the OnRunAsync() method has called StartedAsync(NeonServiceStatus). This is typically used by unit tests to wait until the service is ready before performing tests.
Declaration
public Task WaitUntilStarted()
Returns
Type | Description |
---|---|
Task | The tracking Task. |