Skip to content

Add SystemdServices package that provides Host integration for systemd. #1804

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jun 10, 2019

Conversation

tmds
Copy link
Member

@tmds tmds commented Jun 6, 2019

This package provides a IHostBuilder UseSystemdService method that:

  • changes the Console logger to ConsoleLoggerFormat.Systemd
  • provides SystemdServiceLifetime that triggers shutdown on SIGTERM
  • and which uses the systemd notify protocol to inform systemd when the service
    has started, and when it is stopping.

When the extension method is used outside systemd, it noops.

Contributes to #1241.

This package provides a IHostBuilder UseSystemdService method that:
- changes the Console logger to ConsoleLoggerFormat.Systemd
- provides SystemdServiceLifetime that triggers shutdown on SIGTERM
- and which uses the systemd notify protocol to inform systemd when the service
has started, and when it is stopping.

When the extension method is used outside systemd, it noops.

Contributes to dotnet#1241.
@tmds tmds requested review from analogrelay, Tratcher and a team as code owners June 6, 2019 12:36
@tmds
Copy link
Member Author

tmds commented Jun 6, 2019

The implementation mostly is a copy-paste from Tmds.Systemd adjusted to make it more DI friendly and following style of the WindowsServices package.

I'll add some tests that verify the socket peer receives the expected messages next week.

@tmds
Copy link
Member Author

tmds commented Jun 6, 2019

public bool IsEnabled => _socketPath != null;

/// <inheritdoc />
public void Notify(ServiceState state, ServiceState[] states)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Span<ServiceState>

var endPoint = new UnixDomainSocketEndPoint(_socketPath);
socket.Connect(endPoint);

int rv = socket.Send(data);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Async?
  2. Errors?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about async too, but it's called in a sync context, so there will be waiting anywhere.
Is socket.SendAsync(...).GetAwaiter().GetResult() a better option here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's currently called in a single place but this is a general purpose interface. We may need both sync and async versions.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is socket.SendAsync(...).GetAwaiter().GetResult() a better option here?

Definitely not. I'd make this a NotifyAsync method and allow callers to decide what to do. For example, a caller could decide to fire-and-forget instead of blocking.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can treat this as a sync API. The buffer in the kernel is far larger than what we are trying to send.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know. Can you add a comment for that fact please?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can treat this as a sync API. The buffer in the kernel is far larger than what we are trying to send.

Hrm, we generally avoid operating under assumptions like that, but I see your point. Thoughts @davidfowl ?

public class UseSystemdServiceTests
{
[Fact]
public void DefaultsToOffOutsideOfService()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the strategy for testing this? Kestrel was using Docker to test the systemd activation feature https://github.com/aspnet/AspNetCore/tree/master/src/Servers/Kestrel/test/SystemdActivation, we should probably do the same here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of it could be tested without systemd at all. For example the notifier could be tested by creating a socket, flowing it down (preferably via internals rather than an env-var) and reading from the other end.

But I agree, docker for a simple integration test would be good as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example the notifier could be tested by creating a socket, flowing it down (preferably via internals rather than an env-var) and reading from the other end.

I plan to add such a test.

Kestrel was using Docker to test the systemd activation feature https://github.com/aspnet/AspNetCore/tree/master/src/Servers/Kestrel/test/SystemdActivation, we should probably do the same here.

Are you running these tests on regular basis?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They don't run at all, in fact, I need to file an issue to make them run again.

<TargetFramework>netstandard2.1</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>hosting</PackageTags>
<IsShipping>true</IsShipping>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider this for the linux shared framework? That would make it possible to enable it by default in the worker template (assuming it does what .UseWindowsServices does and only lights up if actually hosted in systemd).

That would also mean it needs to be in source-build.

Of course, I don't know exactly what (if any?) shared framework the worker template references. Does it reference Microsoft.AspNetCore.App or Microsoft.NETCore.App? If the latter, the point is moot, it's not going in to that shared fx in this release. (cc @glennc)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't do this for the windows service so we shouldn't do it here either....

var endPoint = new UnixDomainSocketEndPoint(_socketPath);
socket.Connect(endPoint);

int rv = socket.Send(data);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is socket.SendAsync(...).GetAwaiter().GetResult() a better option here?

Definitely not. I'd make this a NotifyAsync method and allow callers to decide what to do. For example, a caller could decide to fire-and-forget instead of blocking.

public class UseSystemdServiceTests
{
[Fact]
public void DefaultsToOffOutsideOfService()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of it could be tested without systemd at all. For example the notifier could be tested by creating a socket, flowing it down (preferably via internals rather than an env-var) and reading from the other end.

But I agree, docker for a simple integration test would be good as well.

Co-Authored-By: campersau <buchholz.bastian@googlemail.com>
/// <returns></returns>
public static IHostBuilder UseSystemd(this IHostBuilder hostBuilder)
{
if (SystemdHelpers.IsSystemdService ())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (SystemdHelpers.IsSystemdService ())
if (SystemdHelpers.IsSystemdService())

return;
}

using (var socket = new Socket(AddressFamily.Unix, SocketType.Dgram, ProtocolType.Unspecified))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it reasonable to open and close these on demand? Is there a universe where we'd want to keep the connection open because of sending other types of messages?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only for occasional messaging (now: two messages per application lifetime).
The native sd_notify implementation does the same thing: create and close socket per message.
We can change the scope of the socket to the SystemdNotifier instance if it ever makes sense.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

@analogrelay analogrelay added this to the 3.0.0-preview7 milestone Jun 10, 2019
@davidfowl davidfowl merged commit 451fed1 into dotnet:master Jun 10, 2019
@ghost ghost locked as resolved and limited conversation to collaborators May 28, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.