Skip to content
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

Add a socket_service definition #197

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
* [`systemd::modules_load`](#systemd--modules_load): Creates a modules-load.d drop file
* [`systemd::network`](#systemd--network): Creates network config for systemd-networkd
* [`systemd::service_limits`](#systemd--service_limits): Adds a set of custom limits to the service
* [`systemd::socket_service`](#systemd--socket_service): Create a systemd socket activated service
* [`systemd::timer`](#systemd--timer): Create a timer and optionally a service unit to execute with the timer unit
* [`systemd::tmpfile`](#systemd--tmpfile): Creates a systemd tmpfile
* [`systemd::udev::rule`](#systemd--udev--rule): Adds a custom udev rule
Expand Down Expand Up @@ -1333,6 +1334,62 @@ Restart the managed service after setting the limits

Default value: `true`

### <a name="systemd--socket_service"></a>`systemd::socket_service`

Systemd socket activated services have their own dependencies. This is a
convenience wrapper around systemd::unit_file.

#### Parameters

The following parameters are available in the `systemd::socket_service` defined type:

* [`name`](#-systemd--socket_service--name)
* [`ensure`](#-systemd--socket_service--ensure)
* [`socket_content`](#-systemd--socket_service--socket_content)
* [`service_content`](#-systemd--socket_service--service_content)
* [`enable`](#-systemd--socket_service--enable)

##### <a name="-systemd--socket_service--name"></a>`name`

Data type: `Pattern['^[^/]+$']`

The target unit file to create

##### <a name="-systemd--socket_service--ensure"></a>`ensure`

Data type: `Enum['running', 'stopped', 'present', 'absent']`

State of the socket service to ensure. Present means it ensures it's
present, but doesn't ensure the service state.

Default value: `'running'`

##### <a name="-systemd--socket_service--socket_content"></a>`socket_content`

Data type: `Optional[String[1]]`

The content for the socket unit file. Required if ensure isn't absent.

Default value: `undef`

##### <a name="-systemd--socket_service--service_content"></a>`service_content`

Data type: `Optional[String[1]]`

The content for the service unit file. Required if ensure isn't absent.

Default value: `undef`

##### <a name="-systemd--socket_service--enable"></a>`enable`

Data type: `Optional[Boolean]`

Whether to enable or disable the service. By default this is derived from
$ensure but can be overridden for advanced use cases where the service is
running during a migration but shouldn't be enabled on boot.

Default value: `undef`

### <a name="systemd--timer"></a>`systemd::timer`

Create a timer and optionally a service unit to execute with the timer unit
Expand Down
69 changes: 69 additions & 0 deletions manifests/socket_service.pp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# @summary Create a systemd socket activated service
# @api public
#
# Systemd socket activated services have their own dependencies. This is a
# convenience wrapper around systemd::unit_file.
#
# @param name [Pattern['^[^/]+$']]
# The target unit file to create
# @param ensure
# State of the socket service to ensure. Present means it ensures it's
# present, but doesn't ensure the service state.
# @param socket_content
# The content for the socket unit file. Required if ensure isn't absent.
# @param service_content
# The content for the service unit file. Required if ensure isn't absent.
# @param enable
# Whether to enable or disable the service. By default this is derived from
# $ensure but can be overridden for advanced use cases where the service is
# running during a migration but shouldn't be enabled on boot.
define systemd::socket_service (
Enum['running', 'stopped', 'present', 'absent'] $ensure = 'running',
Optional[String[1]] $socket_content = undef,
Optional[String[1]] $service_content = undef,
Optional[Boolean] $enable = undef,
) {
assert_type(Pattern['^[^/]+$'], $name)

if $ensure != 'absent' {
assert_type(NotUndef, $socket_content)
assert_type(NotUndef, $service_content)
}

$active = $ensure ? {
'running' => true,
'stopped' => false,
'absent' => false,
default => undef,
}
# https://tickets.puppetlabs.com/browse/MODULES-11018
if $enable == undef and $active == undef {
$real_enable = undef
} else {
$real_enable = pick($enable, $active)
}

$unit_file_ensure = bool2str($ensure == 'absent', 'absent', 'present')

systemd::unit_file { "${name}.socket":
ensure => $unit_file_ensure,
content => $socket_content,
active => $active,
enable => $real_enable,
}

systemd::unit_file { "${name}.service":
ensure => $unit_file_ensure,
content => $service_content,
active => $active,
enable => $real_enable,
}

if $active != undef or $real_enable != undef {
# Systemd needs both .socket and .service to be loaded when starting the
# service. The unit_file takes care of matching, this ensures the
# non-matching order.
File["/etc/systemd/system/${name}.socket"] -> Service["${name}.service"]
File["/etc/systemd/system/${name}.service"] -> Service["${name}.socket"]
}
}
211 changes: 211 additions & 0 deletions spec/defines/socket_service_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
# frozen_string_literal: true

require 'spec_helper'

describe 'systemd::socket_service' do
let(:title) { 'myservice' }

on_supported_os.each do |os, os_facts|
context "on #{os}" do
let(:facts) { os_facts }

context 'ensure => running' do
let(:params) do
{
ensure: 'running',
socket_content: "[Socket]\nListenStream=/run/myservice.socket\n",
service_content: "[Service]\nType=notify\n",
}
end

it { is_expected.to compile.with_all_deps }

it 'sets up the socket unit file' do
is_expected.to contain_file('/etc/systemd/system/myservice.socket').
with_ensure('file').
with_content(%r{\[Socket\]}).
that_comes_before(['Service[myservice.socket]', 'Service[myservice.service]'])
end

it 'sets up the socket service' do
is_expected.to contain_service('myservice.socket').
with_ensure(true).
with_enable(true)
end

it 'sets up the service unit file' do
is_expected.to contain_file('/etc/systemd/system/myservice.service').
with_ensure('file').
with_content(%r{\[Service\]}).
that_comes_before('Service[myservice.service]')
end

it 'sets up the service service' do
is_expected.to contain_service('myservice.service').
with_ensure(true).
with_enable(true)
end

context 'enable => false' do
let(:params) { super().merge(enable: false) }

it { is_expected.to compile.with_all_deps }

it 'sets up the socket service' do
is_expected.to contain_service('myservice.socket').
with_ensure(true).
with_enable(false)
end

it 'sets up the service service' do
is_expected.to contain_service('myservice.service').
with_ensure(true).
with_enable(false)
end
end
end

context 'ensure => stopped' do
let(:params) do
{
ensure: 'stopped',
socket_content: "[Socket]\nListenStream=/run/myservice.socket\n",
service_content: "[Service]\nType=notify\n",
}
end

it { is_expected.to compile.with_all_deps }

it 'sets up the socket unit file' do
is_expected.to contain_file('/etc/systemd/system/myservice.socket').
with_ensure('file').
with_content(%r{\[Socket\]}).
that_comes_before(['Service[myservice.socket]', 'Service[myservice.service]'])
end

it 'sets up the socket service' do
is_expected.to contain_service('myservice.socket').
with_ensure(false).
with_enable(false)
end

it 'sets up the service unit file' do
is_expected.to contain_file('/etc/systemd/system/myservice.service').
with_ensure('file').
with_content(%r{\[Service\]}).
that_comes_before('Service[myservice.service]')
end

it 'sets up the service service' do
is_expected.to contain_service('myservice.service').
with_ensure(false).
with_enable(false)
end

context 'enable => true' do
let(:params) { super().merge(enable: true) }

it { is_expected.to compile.with_all_deps }

it 'sets up the socket service' do
is_expected.to contain_service('myservice.socket').
with_ensure(false).
with_enable(true)
end

it 'sets up the service service' do
is_expected.to contain_service('myservice.service').
with_ensure(false).
with_enable(true)
end
end
end

context 'ensure => present' do
let(:params) do
{
ensure: 'present',
socket_content: "[Socket]\nListenStream=/run/myservice.socket\n",
service_content: "[Service]\nType=notify\n",
}
end

it { is_expected.to compile.with_all_deps }

it 'sets up the socket unit file' do
is_expected.to contain_file('/etc/systemd/system/myservice.socket').
with_ensure('file').
with_content(%r{\[Socket\]})
end

it "doesn't set up the socket service" do
is_expected.not_to contain_service('myservice.socket')
end

it 'sets up the service unit file' do
is_expected.to contain_file('/etc/systemd/system/myservice.service').
with_ensure('file').
with_content(%r{\[Service\]})
end

it "doesn't set up the service service" do
is_expected.not_to contain_service('myservice.service')
end

context 'enable => true' do
let(:params) { super().merge(enable: true) }

it { is_expected.to compile.with_all_deps }

it 'sets up the socket service' do
is_expected.to contain_service('myservice.socket').
without_ensure.
with_enable(true)
end

it 'sets up the service service' do
is_expected.to contain_service('myservice.service').
without_ensure.
with_enable(true)
end
end
end

context 'ensure => absent' do
let(:params) do
{
ensure: 'absent',
}
end

it { is_expected.to compile.with_all_deps }

it 'sets up the socket unit file' do
is_expected.to contain_file('/etc/systemd/system/myservice.socket').
with_ensure('absent').
without_content.
that_requires('Service[myservice.socket]')
end

it 'sets up the socket service' do
is_expected.to contain_service('myservice.socket').
with_ensure(false).
with_enable(false)
end

it 'sets up the service unit file' do
is_expected.to contain_file('/etc/systemd/system/myservice.service').
with_ensure('absent').
without_content.
that_requires('Service[myservice.service]')
end

it 'sets up the service service' do
is_expected.to contain_service('myservice.service').
with_ensure(false).
with_enable(false)
end
end
end
end
end