Skip to content

Commit

Permalink
Add a socket_service definition
Browse files Browse the repository at this point in the history
For systemd socket activated services it's needed to have both the
.socket and .service unit defintions in place when starting the service.
Prior to 97dd16f the systemctl
daemon-reload took care of it, but now this must be done explicitly.

This new defined type makes it easy and reduces the burden on modules
that define the unit/service pair.
  • Loading branch information
ekohl committed May 31, 2021
1 parent 81f3eb5 commit a8b6d49
Show file tree
Hide file tree
Showing 2 changed files with 248 additions and 0 deletions.
61 changes: 61 additions & 0 deletions manifests/socket_service.pp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# @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 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,
}
$real_enable = pick_default($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 or $real_enable {
# 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"]
}
}
187 changes: 187 additions & 0 deletions spec/defines/socket_service_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
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

0 comments on commit a8b6d49

Please sign in to comment.