diff --git a/README.md b/README.md index f8c55fe..c4091e0 100644 --- a/README.md +++ b/README.md @@ -1,346 +1,382 @@ # Systemd [![Puppet Forge](http://img.shields.io/puppetforge/v/camptocamp/systemd.svg)](https://forge.puppetlabs.com/camptocamp/systemd) [![Puppet Forge Downloads](http://img.shields.io/puppetforge/dt/camptocamp/systemd.svg)](https://forge.puppetlabs.com/camptocamp/systemd) [![Build Status](https://travis-ci.org/camptocamp/puppet-systemd.png?branch=master)](https://travis-ci.org/camptocamp/puppet-systemd) [![Puppet Forge Endorsement](https://img.shields.io/puppetforge/e/camptocamp/systemd.svg)](https://forge.puppetlabs.com/camptocamp/systemd) [![By Camptocamp](https://img.shields.io/badge/by-camptocamp-fb7047.svg)](http://www.camptocamp.com) ## Overview This module declares exec resources to create global sync points for reloading systemd. **Version 2 and newer of the module don't work with Hiera 3! You need to migrate your existing Hiera setup to Hiera 5** ## Usage and examples There are two ways to use this module. ### unit files Let this module handle file creation and systemd reloading. ```puppet systemd::unit_file { 'foo.service': source => "puppet:///modules/${module_name}/foo.service", } ~> service {'foo': ensure => 'running', } ``` Or handle file creation yourself and trigger systemd. ```puppet include systemd::systemctl::daemon_reload file { '/usr/lib/systemd/system/foo.service': ensure => file, owner => 'root', group => 'root', mode => '0644', source => "puppet:///modules/${module_name}/foo.service", } ~> Class['systemd::systemctl::daemon_reload'] service {'foo': ensure => 'running', subscribe => File['/usr/lib/systemd/system/foo.service'], } ``` You can also use this module to more fully manage the new unit. This example deploys the unit, reloads systemd and then enables and starts it. ```puppet systemd::unit_file { 'foo.service': source => "puppet:///modules/${module_name}/foo.service", enable => true, active => true, } ``` ### drop-in files Drop-in files are used to add or alter settings of a unit without modifying the unit itself. As for the unit files, the module can handle the file and directory creation and systemd reloading: ```puppet systemd::dropin_file { 'foo.conf': unit => 'foo.service', source => "puppet:///modules/${module_name}/foo.conf", } ~> service {'foo': ensure => 'running', } ``` Or handle file and directory creation yourself and trigger systemd: ```puppet include systemd::systemctl::daemon_reload file { '/etc/systemd/system/foo.service.d': ensure => directory, owner => 'root', group => 'root', } file { '/etc/systemd/system/foo.service.d/foo.conf': ensure => file, owner => 'root', group => 'root', mode => '0644', source => "puppet:///modules/${module_name}/foo.conf", } ~> Class['systemd::systemctl::daemon_reload'] service {'foo': ensure => 'running', subscribe => File['/etc/systemd/system/foo.service.d/foo.conf'], } ``` Sometimes it's desirable to reload the systemctl daemon before a service is refreshed (for example: when overriding `ExecStart` or adding environment variables to the drop-in file). In that case, use `daemon_reload => 'eager'` instead of the default `'lazy'`. Be aware that the daemon could be reloaded multiple times if you have multiple `systemd::dropin_file` resources and any one of them is using `'eager'`. dropin-files can also be generated via hiera: ```yaml systemd::dropin_files: my-foo.conf: unit: foo.service source: puppet:///modules/${module_name}/foo.conf ``` ### tmpfiles Let this module handle file creation and systemd reloading ```puppet systemd::tmpfile { 'foo.conf': source => "puppet:///modules/${module_name}/foo.conf", } ``` Or handle file creation yourself and trigger systemd. ```puppet include systemd::tmpfiles file { '/etc/tmpfiles.d/foo.conf': ensure => file, owner => 'root', group => 'root', mode => '0644', source => "puppet:///modules/${module_name}/foo.conf", } ~> Class['systemd::tmpfiles'] ``` ### timer units Create a systemd timer unit and a systemd service unit to execute from that timer The following will create a timer unit and a service unit file. The execution of `systemctl daemon-reload` will occur. When `active` and `enable` are set to `true` the puppet service `runoften.timer` will be declared, started and enabled. ```puppet systemd::timer{'runoften.timer': timer_source => "puppet:///modules/${module_name}/runoften.timer", service_source => "puppet:///modules/${module_name}/runoften.service", active => true, enable => true, } ``` A trivial daily run. In this case enable and active are both unset and so the service `daily.timer` is not declared by the `systemd::timer` type. ```puppet $_timer = @(EOT) [Timer] OnCalendar=daily RandomizedDelaySec=1d EOT $_service = @(EOT) [Service] Type=oneshot ExecStart=/usr/bin/touch /tmp/file EOT systemd::timer{'daily.timer': timer_content => $_timer, service_content => $_service, } service{'daily.timer': ensure => running, subscribe => Systemd::Timer['daily.timer'], } ``` If neither `service_content` or `service_source` are specified then no service unit will be created. The service unit name can also be specified. ```puppet $_timer = @(EOT) [Timer] OnCalendar=daily RandomizedDelaySec=1d Unit=touch-me-today.service EOT $_service = @(EOT) [Service] Type=oneshot ExecStart=/usr/bin/touch /tmp/file EOT systemd::timer{'daily.timer': timer_content => $_timer, service_unit => 'touch-me-today.service', service_content => $_service, active => true, enable => true, } ``` ### service limits Manage soft and hard limits on various resources for executed processes. ```puppet systemd::service_limits { 'foo.service': limits => { 'LimitNOFILE' => 8192, 'LimitNPROC' => 16384, } } ``` Or provide the configuration file yourself. Systemd reloading and restarting of the service are handled by the module. ```puppet systemd::service_limits { 'foo.service': source => "puppet:///modules/${module_name}/foo.conf", } ``` ### network systemd-networkd is able to manage your network configuration. We provide a defined resource which can write the interface configurations. systemd-networkd needs to be restarted to apply the configs. The defined resource can do this for you: ```puppet systemd::network{'eth0.network': source => "puppet:///modules/${module_name}/eth0.network", restart_service => true, } ``` ### Services Systemd provides multiple services. Currently you can manage `systemd-resolved`, `systemd-timesyncd`, `systemd-networkd`, `systemd-journald` and `systemd-logind` via the main class: ```puppet class{'systemd': manage_resolved => true, manage_networkd => true, manage_timesyncd => true, manage_journald => true, + manage_udevd => true, manage_logind => true, } ``` $manage_networkd is required if you want to reload it for new `systemd::network` resources. Setting $manage_resolved will also manage your `/etc/resolv.conf`. When configuring `systemd::resolved` you could set `dns_stub_resolver` to false (default) to use a *standard* `/etc/resolved.conf`, or you could set it to `true` to use the local resolver provided by `systemd-resolved`. Systemd has introduced `DNS Over TLS` in the release 239. Currently two states are supported `no` and `opportunistic`. When enabled with `opportunistic` `systemd-resolved` will start a TCP-session to a DNS server with `DNS Over TLS` support. Note that there will be no host checking for `DNS Over TLS` due to missing implementation in `systemd-resolved`. It is possible to configure the default ntp servers in `/etc/systemd/timesyncd.conf`: ```puppet class{'systemd': manage_timesyncd => true, ntp_server => ['0.pool.ntp.org', '1.pool.ntp.org'], fallback_ntp_server => ['2.pool.ntp.org', '3.pool.ntp.org'], } ``` This requires [puppetlabs-inifile](https://forge.puppet.com/puppetlabs/inifile), which is only a soft dependency in this module (you need to explicitly install it). Both parameters accept a string or an array. ### Resource Accounting Systemd has support for different accounting option. It can track CPU/Memory/Network stats per process. This is explained in depth at [systemd-system.conf](https://www.freedesktop.org/software/systemd/man/systemd-system.conf.html). This defaults to off (default on most operating systems). You can enable this with the `$manage_accounting` parameter. The module provides a default set of working accounting options per operating system, but you can still modify them with `$accounting`: ```puppet class{'systemd': manage_accounting => true, accounting => { 'DefaultCPUAccounting' => 'yes', 'DefaultMemoryAccounting' => 'no', } } ``` ### journald configuration It also allows you to manage journald settings. You can manage journald settings through setting the `journald_settings` parameter. If you want a parameter to be removed, you can pass its value as params. ```yaml systemd::journald_settings: Storage: auto MaxRetentionSec: 5day MaxLevelStore: ensure: absent ``` +### udevd configuration + +It allows you to manage the udevd configuration. You can set the udev.conf values via the `udev_log`, `udev_children_max`, `udev_exec_delay`, `udev_event_timeout`, `udev_resolve_names`, and `udev_timeout_signal` parameters. + +Additionally you can set custom udev rules with the `udev_rules` parameter. + +```puppet +class { 'systemd': + manage_udevd => true, + udev_rules => { + 'example_raw.rules' => { + 'rules' => [ + 'ACTION=="add", KERNEL=="sda", RUN+="/bin/raw /dev/raw/raw1 %N"', + 'ACTION=="add", KERNEL=="sdb", RUN+="/bin/raw /dev/raw/raw2 %N"', + ], + }, + }, +} +``` + +### udev::rules configuration + +Custom udev rules can be defined for specific events. + +```yaml +systemd::udev::rule: + ensure: present + path: /etc/udev/rules.d + selinux_ignore_defaults: false + notify: "Service[systemd-udevd']" + rules: + - 'ACTION=="add", KERNEL=="sda", RUN+="/bin/raw /dev/raw/raw1 %N"' + - 'ACTION=="add", KERNEL=="sdb", RUN+="/bin/raw /dev/raw/raw2 %N"', +``` + ### logind configuration It also allows you to manage logind settings. You can manage logind settings through setting the `logind_settings` parameter. If you want a parameter to be removed, you can pass its value as params. ```yaml systemd::logind_settings: HandleSuspendKey: 'ignore' KillUserProcesses: 'no' RemoveIPC: ensure: absent UserTasksMax: 10000 ``` ### User linger A `loginctl_user` resource is available to manage user linger enablement: ```puppet loginctl_user { 'foo': linger => enabled, } ``` or as a hash via the `systemd::loginctl_users` parameter. diff --git a/data/common.yaml b/data/common.yaml index ed43759..f95f370 100644 --- a/data/common.yaml +++ b/data/common.yaml @@ -1,27 +1,34 @@ --- systemd::service_limits: {} systemd::manage_resolved: false systemd::resolved_ensure: 'running' systemd::dns: ~ systemd::fallback_dns: ~ systemd::domains: ~ systemd::llmnr: ~ systemd::multicast_dns: ~ systemd::dnssec: ~ systemd::dnsovertls: false systemd::cache: false systemd::dns_stub_listener: ~ systemd::use_stub_resolver: false systemd::manage_networkd: false systemd::networkd_ensure: 'running' systemd::manage_timesyncd: false systemd::timesyncd_ensure: 'running' systemd::ntp_server: ~ systemd::fallback_ntp_server: ~ systemd::manage_accounting: false systemd::accounting: {} systemd::purge_dropin_dirs: true systemd::manage_journald: true systemd::journald_settings: {} +systemd::manage_udevd: false +systemd::udev_log: ~ +systemd::udev_children_max: ~ +systemd::udev_exec_delay: ~ +systemd::udev_event_timeout: ~ +systemd::udev_timeout_signal: ~ +systemd::udev_resolve_names: ~ systemd::manage_logind: false systemd::logind_settings: {} diff --git a/manifests/init.pp b/manifests/init.pp index 89925a1..6308e16 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -1,153 +1,190 @@ # This module allows triggering systemd commands once for all modules # # @api public # # @param service_limits # May be passed a resource hash suitable for passing directly into the # ``create_resources()`` function as called on ``systemd::service_limits`` # # @param manage_resolved # Manage the systemd resolver # # @param resolved_ensure # The state that the ``resolved`` service should be in # # @param dns # A space-separated list of IPv4 and IPv6 addresses to use as system DNS servers. # DNS requests are sent to one of the listed DNS servers in parallel to suitable # per-link DNS servers acquired from systemd-networkd.service(8) or set at runtime # by external applications. requires puppetlabs-inifile # # @param fallback_dns # A space-separated list of IPv4 and IPv6 addresses to use as the fallback DNS # servers. Any per-link DNS servers obtained from systemd-networkd take # precedence over this setting. requires puppetlabs-inifile # # @param domains # A space-separated list of domains host names or IP addresses to be used # systemd-resolved take precedence over this setting. # # @param llmnr # Takes a boolean argument or "resolve". # # @param multicast_dns # Takes a boolean argument or "resolve". # # @param dnssec # Takes a boolean argument or "allow-downgrade". # # @param dnsovertls # Takes a boolean argument or "opportunistic" # # @param cache # Takes a boolean argument or "no-negative". # # @param dns_stub_listener # Takes a boolean argument or one of "udp" and "tcp". # # @param use_stub_resolver # Takes a boolean argument. When "false" (default) it uses /var/run/systemd/resolve/resolv.conf # as /etc/resolv.conf. When "true", it uses /var/run/systemd/resolve/stub-resolv.conf # @param manage_networkd # Manage the systemd network daemon # # @param networkd_ensure # The state that the ``networkd`` service should be in # # @param manage_timesyncd # Manage the systemd tiemsyncd daemon # # @param timesyncd_ensure # The state that the ``timesyncd`` service should be in # # @param ntp_server # comma separated list of ntp servers, will be combined with interface specific # addresses from systemd-networkd. requires puppetlabs-inifile # # @param fallback_ntp_server # A space-separated list of NTP server host names or IP addresses to be used # as the fallback NTP servers. Any per-interface NTP servers obtained from # systemd-networkd take precedence over this setting. requires puppetlabs-inifile # # @param manage_journald # Manage the systemd journald # # @param journald_settings # Config Hash that is used to configure settings in journald.conf # +# @param manage_udevd +# Manage the systemd udev daemon +# +# @param udev_log +# The value of /etc/udev/udev.conf udev_log +# +# @param udev_children_max +# The value of /etc/udev/udev.conf children_max +# +# @param udev_exec_delay +# The value of /etc/udev/udev.conf exec_delay +# +# @param udev_event_timeout +# The value of /etc/udev/udev.conf event_timeout +# +# @param udev_resolve_names +# The value of /etc/udev/udev.conf resolve_names +# +# @param udev_timeout_signal +# The value of /etc/udev/udev.conf timeout_signal +# +# @param udev_rules +# Config Hash that is used to generate instances of our +# `udev::rule` define. +# # @param manage_logind # Manage the systemd logind # # @param logind_settings # Config Hash that is used to configure settings in logind.conf # # @param loginctl_users # Config Hash that is used to generate instances of our type # `loginctl_user`. # # @param dropin_files # Configure dropin files via hiera with factory pattern class systemd ( Hash[String,Hash[String, Any]] $service_limits, Boolean $manage_resolved, Enum['stopped','running'] $resolved_ensure, Optional[Variant[Array[String],String]] $dns, Optional[Variant[Array[String],String]] $fallback_dns, Optional[Variant[Array[String],String]] $domains, Optional[Variant[Boolean,Enum['resolve']]] $llmnr, Optional[Variant[Boolean,Enum['resolve']]] $multicast_dns, Optional[Variant[Boolean,Enum['allow-downgrade']]] $dnssec, Optional[Variant[Boolean,Enum['opportunistic', 'no']]] $dnsovertls, Optional[Variant[Boolean,Enum['no-negative']]] $cache, Optional[Variant[Boolean,Enum['udp','tcp']]] $dns_stub_listener, Boolean $use_stub_resolver, Boolean $manage_networkd, Enum['stopped','running'] $networkd_ensure, Boolean $manage_timesyncd, Enum['stopped','running'] $timesyncd_ensure, Optional[Variant[Array,String]] $ntp_server, Optional[Variant[Array,String]] $fallback_ntp_server, Boolean $manage_accounting, Hash[String,String] $accounting, Boolean $purge_dropin_dirs, Boolean $manage_journald, Systemd::JournaldSettings $journald_settings, + Boolean $manage_udevd, + Optional[Variant[Integer,String]] $udev_log, + Optional[Integer] $udev_children_max, + Optional[Integer] $udev_exec_delay, + Optional[Integer] $udev_event_timeout, + Optional[Enum['early', 'late', 'never']] $udev_resolve_names, + Optional[Variant[Integer,String]] $udev_timeout_signal, Boolean $manage_logind, Systemd::LogindSettings $logind_settings, Hash $loginctl_users = {}, Hash $dropin_files = {}, + Hash $udev_rules = {}, ) { contain systemd::systemctl::daemon_reload create_resources('systemd::service_limits', $service_limits) if $manage_resolved and $facts['systemd_internal_services'] and $facts['systemd_internal_services']['systemd-resolved.service'] { contain systemd::resolved } if $manage_networkd and $facts['systemd_internal_services'] and $facts['systemd_internal_services']['systemd-networkd.service'] { contain systemd::networkd } if $manage_timesyncd and $facts['systemd_internal_services'] and $facts['systemd_internal_services']['systemd-timesyncd.service'] { contain systemd::timesyncd } + if $manage_udevd { + contain systemd::udevd + } + if $manage_accounting { contain systemd::system } if $manage_journald { contain systemd::journald } if $manage_logind { contain systemd::logind } $dropin_files.each |$name, $resource| { systemd::dropin_file { $name: * => $resource, } } } diff --git a/manifests/udev/rule.pp b/manifests/udev/rule.pp new file mode 100644 index 0000000..adf027d --- /dev/null +++ b/manifests/udev/rule.pp @@ -0,0 +1,48 @@ +# Adds a custom udev rule +# +# @api public +# +# @see udev(7) +# +# @attr name [Pattern['^.+\.rules$']] +# The name of the udev rules to create +# +# @param $ensure +# Whether to drop a file or remove it +# +# @param path +# The path to the main systemd settings directory +# +# @param selinux_ignore_defaults +# If Puppet should ignore the default SELinux labels. +# +# @param notify_services +# List of services to notify when this rule is updated +# +# @param rules +# The literal udev rules you want to deploy +# +define systemd::udev::rule ( + Array $rules, + Enum['present', 'absent', 'file'] $ensure = 'present', + Stdlib::Absolutepath $path = '/etc/udev/rules.d', + Optional[Variant[Array, String]] $notify_services = [], + Optional[Boolean] $selinux_ignore_defaults = false, +) { + include systemd + + $filename = assert_type(Pattern['^.+\.rules$'], $name) |$expected, $actual| { + fail("The \$name should match \'${expected}\', you passed \'${actual}\'") + } + + file { $filename: + ensure => $ensure, + owner => 'root', + group => 'root', + mode => '0444', + path => join([$path, $name], '/'), + notify => $notify_services, + selinux_ignore_defaults => $selinux_ignore_defaults, + content => epp("${module_name}/udev_rule.epp", {'rules' => $rules}), + } +} diff --git a/manifests/udevd.pp b/manifests/udevd.pp new file mode 100644 index 0000000..2fc9fd5 --- /dev/null +++ b/manifests/udevd.pp @@ -0,0 +1,35 @@ +# @api private +# +# This class manages systemd's udev config +# +# https://www.freedesktop.org/software/systemd/man/udev.conf.html +class systemd::udevd { + assert_private() + + service { 'systemd-udevd': + ensure => running, + enable => true, + } + + file { '/etc/udev/udev.conf': + ensure => 'file', + owner => 'root', + group => 'root', + mode => '0444', + content => epp("${module_name}/udev_conf.epp", { + 'udev_log' => $systemd::udev_log, + 'udev_children_max' => $systemd::udev_children_max, + 'udev_exec_delay' => $systemd::udev_exec_delay, + 'udev_event_timeout' => $systemd::udev_event_timeout, + 'udev_resolve_names' => $systemd::udev_resolve_names, + 'udev_timeout_signal' => $systemd::udev_timeout_signal, + }), + notify => Service['systemd-udevd'], + } + + $systemd::udev_rules.each |$udev_rule_name, $udev_rule| { + systemd::udev::rule { $udev_rule_name: + * => $udev_rule, + } + } +} diff --git a/spec/classes/init_spec.rb b/spec/classes/init_spec.rb index 059cb60..6c189ce 100644 --- a/spec/classes/init_spec.rb +++ b/spec/classes/init_spec.rb @@ -1,350 +1,446 @@ require 'spec_helper' describe 'systemd' do context 'supported operating systems' do on_supported_os.each do |os, facts| context "on #{os}" do let(:facts) { facts } it { is_expected.to compile.with_all_deps } it { is_expected.to create_class('systemd') } it { is_expected.to create_class('systemd::systemctl::daemon_reload') } it { is_expected.to contain_class('systemd::journald') } it { is_expected.to create_service('systemd-journald') } it { is_expected.to have_ini_setting_resource_count(0) } it { is_expected.not_to create_service('systemd-resolved') } it { is_expected.not_to create_service('systemd-networkd') } it { is_expected.not_to create_service('systemd-timesyncd') } context 'when enabling resolved and networkd' do let(:params) do { manage_resolved: true, manage_networkd: true, } end it { is_expected.to create_service('systemd-resolved').with_ensure('running') } it { is_expected.to create_service('systemd-resolved').with_enable(true) } it { is_expected.to create_service('systemd-networkd').with_ensure('running') } it { is_expected.to create_service('systemd-networkd').with_enable(true) } end context 'when enabling resolved with DNS values (string)' do let(:params) do { manage_resolved: true, dns: '8.8.8.8 8.8.4.4', fallback_dns: '2001:4860:4860::8888 2001:4860:4860::8844', } end it { is_expected.to create_service('systemd-resolved').with_ensure('running') } it { is_expected.to create_service('systemd-resolved').with_enable(true) } it { is_expected.to contain_ini_setting('dns') } it { is_expected.to contain_ini_setting('fallback_dns') } it { is_expected.not_to contain_ini_setting('domains') } it { is_expected.not_to contain_ini_setting('multicast_dns') } it { is_expected.not_to contain_ini_setting('llmnr') } it { is_expected.not_to contain_ini_setting('dnssec') } it { is_expected.not_to contain_ini_setting('dnsovertls') } it { is_expected.not_to contain_ini_setting('cache') } it { is_expected.not_to contain_ini_setting('dns_stub_listener') } end context 'when enabling resolved with DNS values (array)' do let(:params) do { manage_resolved: true, dns: ['8.8.8.8', '8.8.4.4'], fallback_dns: ['2001:4860:4860::8888', '2001:4860:4860::8844'], } end it { is_expected.to create_service('systemd-resolved').with_ensure('running') } it { is_expected.to create_service('systemd-resolved').with_enable(true) } it { is_expected.to contain_ini_setting('dns') } it { is_expected.to contain_ini_setting('fallback_dns') } it { is_expected.not_to contain_ini_setting('domains') } it { is_expected.not_to contain_ini_setting('multicast_dns') } it { is_expected.not_to contain_ini_setting('llmnr') } it { is_expected.not_to contain_ini_setting('dnssec') } it { is_expected.not_to contain_ini_setting('dnsovertls') } it { is_expected.not_to contain_ini_setting('cache') } it { is_expected.not_to contain_ini_setting('dns_stub_listener') } end context 'when enabling resolved with DNS values (full)' do let(:params) do { manage_resolved: true, dns: ['8.8.8.8', '8.8.4.4'], fallback_dns: ['2001:4860:4860::8888', '2001:4860:4860::8844'], domains: ['2001:4860:4860::8888', '2001:4860:4860::8844'], llmnr: true, multicast_dns: false, dnssec: false, dnsovertls: 'no', cache: true, dns_stub_listener: 'udp', } end it { is_expected.to create_service('systemd-resolved').with_ensure('running') } it { is_expected.to create_service('systemd-resolved').with_enable(true) } it { is_expected.to contain_ini_setting('dns') } it { is_expected.to contain_ini_setting('fallback_dns') } it { is_expected.to contain_ini_setting('domains') } it { is_expected.to contain_ini_setting('multicast_dns') } it { is_expected.to contain_ini_setting('llmnr') } it { is_expected.to contain_ini_setting('dnssec') } it { is_expected.to contain_ini_setting('dnsovertls') } it { is_expected.to contain_ini_setting('cache').with( path: '/etc/systemd/resolved.conf', value: 'yes', ) } it { is_expected.to contain_ini_setting('dns_stub_listener') } end context 'when enabling resolved with no-negative cache variant' do let(:params) do { manage_resolved: true, cache: 'no-negative', } end it { is_expected.to create_service('systemd-resolved').with_ensure('running') } it { is_expected.to create_service('systemd-resolved').with_enable(true) } it { is_expected.to contain_ini_setting('cache').with( path: '/etc/systemd/resolved.conf', value: 'no-negative', ) } end context 'when enabling timesyncd' do let(:params) do { manage_timesyncd: true, } end it { is_expected.to create_service('systemd-timesyncd').with_ensure('running') } it { is_expected.to create_service('systemd-timesyncd').with_enable(true) } it { is_expected.not_to create_service('systemd-resolved').with_ensure('running') } it { is_expected.not_to create_service('systemd-resolved').with_enable(true) } it { is_expected.not_to create_service('systemd-networkd').with_ensure('running') } it { is_expected.not_to create_service('systemd-networkd').with_enable(true) } end context 'when enabling timesyncd with NTP values (string)' do let(:params) do { manage_timesyncd: true, ntp_server: '0.pool.ntp.org 1.pool.ntp.org', fallback_ntp_server: '2.pool.ntp.org 3.pool.ntp.org', } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_ini_setting('ntp_server') } it { is_expected.to contain_ini_setting('fallback_ntp_server') } end context 'when enabling timesyncd with NTP values (array)' do let(:params) do { manage_timesyncd: true, ntp_server: ['0.pool.ntp.org', '1.pool.ntp.org'], fallback_ntp_server: ['2.pool.ntp.org', '3.pool.ntp.org'], } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_ini_setting('ntp_server') } it { is_expected.to contain_ini_setting('fallback_ntp_server') } end context 'when passing service limits' do let(:params) do { service_limits: { 'openstack-nova-compute.service' => { 'limits' => { 'LimitNOFILE' => 32_768 } } }, } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_systemd__service_limits('openstack-nova-compute.service').with_limits('LimitNOFILE' => 32_768) } end context 'when managing Accounting options' do let :params do { manage_accounting: true, } end it { is_expected.to contain_class('systemd::system') } case facts[:os]['family'] when 'Archlinux' accounting = ['DefaultCPUAccounting', 'DefaultIOAccounting', 'DefaultIPAccounting', 'DefaultBlockIOAccounting', 'DefaultMemoryAccounting', 'DefaultTasksAccounting'] when 'Debian' accounting = ['DefaultCPUAccounting', 'DefaultBlockIOAccounting', 'DefaultMemoryAccounting'] when 'RedHat' accounting = ['DefaultCPUAccounting', 'DefaultBlockIOAccounting', 'DefaultMemoryAccounting', 'DefaultTasksAccounting'] when 'Suse' accounting = ['DefaultCPUAccounting', 'DefaultBlockIOAccounting', 'DefaultMemoryAccounting', 'DefaultTasksAccounting'] end accounting.each do |account| it { is_expected.to contain_ini_setting(account) } end it { is_expected.to compile.with_all_deps } end context 'when enabling journald with options' do let(:params) do { manage_journald: true, journald_settings: { 'Storage' => 'auto', 'MaxRetentionSec' => '5day', 'MaxLevelStore' => { 'ensure' => 'absent', }, }, } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_service('systemd-journald').with( ensure: 'running', ) } it { is_expected.to have_ini_setting_resource_count(3) } it { is_expected.to contain_ini_setting('Storage').with( path: '/etc/systemd/journald.conf', section: 'Journal', notify: 'Service[systemd-journald]', value: 'auto', ) } it { is_expected.to contain_ini_setting('MaxRetentionSec').with( path: '/etc/systemd/journald.conf', section: 'Journal', notify: 'Service[systemd-journald]', value: '5day', ) } it { is_expected.to contain_ini_setting('MaxLevelStore').with( path: '/etc/systemd/journald.conf', section: 'Journal', notify: 'Service[systemd-journald]', ensure: 'absent', ) } end context 'when disabling journald' do let(:params) do { manage_journald: false, } end it { is_expected.to compile.with_all_deps } it { is_expected.not_to contain_service('systemd-journald') } end + context 'when disabling udevd management' do + let(:params) do + { + manage_udevd: false, + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.not_to contain_service('systemd-udevd') } + it { is_expected.not_to contain_file('/etc/udev/udev.conf') } + end + + context 'when working with udevd and no custom rules' do + let(:params) do + { + manage_udevd: true, + udev_log: 'daemon', + udev_children_max: 1, + udev_exec_delay: 2, + udev_event_timeout: 3, + udev_resolve_names: 'early', + udev_timeout_signal: 'SIGKILL', + } + end + + it { is_expected.to compile.with_all_deps } + it { + is_expected.to contain_service('systemd-udevd') + .with(enable: true, + ensure: 'running') + } + it { + is_expected.to contain_file('/etc/udev/udev.conf') + .with(ensure: 'file', + owner: 'root', + group: 'root', + mode: '0444') + .with_content(%r{^udev_log=daemon$}) + .with_content(%r{^children_max=1$}) + .with_content(%r{^exec_delay=2$}) + .with_content(%r{^event_timeout=3$}) + .with_content(%r{^resolve_names=early$}) + .with_content(%r{^timeout_signal=SIGKILL$}) + } + end + + context 'when working with udevd and a rule set' do + let(:params) do + { + manage_udevd: true, + udev_log: 'daemon', + udev_children_max: 1, + udev_exec_delay: 2, + udev_event_timeout: 3, + udev_resolve_names: 'early', + udev_timeout_signal: 'SIGKILL', + udev_rules: { 'example_raw.rules' => { + 'rules' => [ + '# I am a comment', + 'ACTION=="add", KERNEL=="sda", RUN+="/bin/raw /dev/raw/raw1 %N"', + 'ACTION=="add", KERNEL=="sdb", RUN+="/bin/raw /dev/raw/raw2 %N"', + ], + } }, + + } + end + + it { is_expected.to compile.with_all_deps } + it { + is_expected.to contain_service('systemd-udevd') + .with(enable: true, + ensure: 'running') + } + it { + is_expected.to contain_file('/etc/udev/udev.conf') + .with(ensure: 'file', + owner: 'root', + group: 'root', + mode: '0444') + .with_content(%r{^udev_log=daemon$}) + .with_content(%r{^children_max=1$}) + .with_content(%r{^exec_delay=2$}) + .with_content(%r{^event_timeout=3$}) + .with_content(%r{^resolve_names=early$}) + .with_content(%r{^timeout_signal=SIGKILL$}) + } + it { + is_expected.to contain_systemd__udev__rule('example_raw.rules') + .with(rules: [ + '# I am a comment', + 'ACTION=="add", KERNEL=="sda", RUN+="/bin/raw /dev/raw/raw1 %N"', + 'ACTION=="add", KERNEL=="sdb", RUN+="/bin/raw /dev/raw/raw2 %N"', + ]) + } + end + context 'when enabling logind with options' do let(:params) do { manage_logind: true, logind_settings: { 'HandleSuspendKey' => 'ignore', 'KillUserProcesses' => 'no', 'KillExcludeUsers' => ['a', 'b'], 'RemoveIPC' => { 'ensure' => 'absent', }, 'UserTasksMax' => '10000', }, loginctl_users: { 'foo' => { 'linger' => 'enabled' }, }, } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_service('systemd-logind').with( ensure: 'running', ) } it { is_expected.to have_ini_setting_resource_count(5) } it { is_expected.to contain_ini_setting('HandleSuspendKey').with( path: '/etc/systemd/logind.conf', section: 'Login', notify: 'Service[systemd-logind]', value: 'ignore', ) } it { is_expected.to contain_ini_setting('KillUserProcesses').with( path: '/etc/systemd/logind.conf', section: 'Login', notify: 'Service[systemd-logind]', value: 'no', ) } it { is_expected.to contain_ini_setting('KillExcludeUsers').with( path: '/etc/systemd/logind.conf', section: 'Login', notify: 'Service[systemd-logind]', value: 'a b', ) } it { is_expected.to contain_ini_setting('RemoveIPC').with( path: '/etc/systemd/logind.conf', section: 'Login', notify: 'Service[systemd-logind]', ensure: 'absent', ) } it { is_expected.to contain_ini_setting('UserTasksMax').with( path: '/etc/systemd/logind.conf', section: 'Login', notify: 'Service[systemd-logind]', value: '10000', ) } it { is_expected.to contain_loginctl_user('foo').with(linger: 'enabled') } end context 'when passing dropin_files' do let(:params) do { dropin_files: { 'my-foo.conf' => { 'unit' => 'foo.service', 'content' => '[Service]\nReadWritePaths=/', }, }, } end it { is_expected.to contain_systemd__dropin_file('my-foo.conf').with_content('[Service]\nReadWritePaths=/') } end end end end end diff --git a/spec/defines/udev_rules.spec b/spec/defines/udev_rules.spec new file mode 100644 index 0000000..8372833 --- /dev/null +++ b/spec/defines/udev_rules.spec @@ -0,0 +1,77 @@ +require 'spec_helper' + +describe 'systemd::udev::rule' do + context 'supported operating systems' do + on_supported_os.each do |os, facts| + context "on #{os}" do + let(:facts) { facts } + + let(:title) { 'test.rules' } + + describe 'with all options (one notify)' do + let(:params) do + { + ensure: 'present', + path: '/etc/udev/rules.d', + selinux_ignore_defaults: false, + notify_services: "Service['systemd-udevd']", + rules: [ + '# I am a comment', + 'ACTION=="add", KERNEL=="sda", RUN+="/bin/raw /dev/raw/raw1 %N"', + 'ACTION=="add", KERNEL=="sdb", RUN+="/bin/raw /dev/raw/raw2 %N"', + ], + } + end + + it { is_expected.to compile.with_all_deps } + it { + is_expected.to create_file("/etc/udev/rules.d/#{title}") + .with(ensure: 'file', mode: '0444', owner: 'root', group: 'root') + .with_content(%r{^# I am a comment$}) + .with_content(%r{^ACTION=="add", KERNEL=="sda", RUN+="/bin/raw /dev/raw/raw1 %N"$}) + .with_content(%r{^ACTION=="add", KERNEL=="sdb", RUN+="/bin/raw /dev/raw/raw2 %N"$}) + .that_notifies("Service['systemd-udevd']") + } + end + + describe 'with all options (array notify)' do + let(:params) do + { + ensure: 'present', + path: '/etc/udev/rules.d', + selinux_ignore_defaults: false, + notify_services: ["Service['systemd-udevd']", "Service['foo']"], + rules: [ + '# I am a comment', + 'ACTION=="add", KERNEL=="sda", RUN+="/bin/raw /dev/raw/raw1 %N"', + 'ACTION=="add", KERNEL=="sdb", RUN+="/bin/raw /dev/raw/raw2 %N"', + ], + } + end + + it { is_expected.to compile.with_all_deps } + it { + is_expected.to create_file("/etc/udev/rules.d/#{title}") + .with(ensure: 'file', mode: '0444', owner: 'root', group: 'root') + .with_content(%r{^# I am a comment$}) + .with_content(%r{^ACTION=="add", KERNEL=="sda", RUN+="/bin/raw /dev/raw/raw1 %N"$}) + .with_content(%r{^ACTION=="add", KERNEL=="sdb", RUN+="/bin/raw /dev/raw/raw2 %N"$}) + .that_notifies("Service['systemd-udevd']") + .that_notifies("Service['foo']") + } + end + + describe 'ensured absent' do + let(:params) { { ensure: 'absent' } } + + it { is_expected.to compile.with_all_deps } + it do + is_expected.to create_file("/etc/udev/rules.d/#{title}") + .with_ensure('absent') + .that_notifies("Service['systemd-udevd']") + end + end + end + end + end +end diff --git a/templates/udev_conf.epp b/templates/udev_conf.epp new file mode 100644 index 0000000..4250dbb --- /dev/null +++ b/templates/udev_conf.epp @@ -0,0 +1,31 @@ +<%- | + Optional[Variant[Integer,String]] $udev_log, + Optional[Integer] $udev_children_max, + Optional[Integer] $udev_exec_delay, + Optional[Integer] $udev_event_timeout, + Optional[Enum['early', 'late', 'never']] $udev_resolve_names, + Optional[Variant[Integer,String]] $udev_timeout_signal +| -%> +# This file managed by Puppet - DO NOT EDIT +# +# The initial syslog(3) priority: "err", "info", "debug" or its +# numerical equivalent. For runtime debugging, the daemons internal +# state can be changed with: "udevadm control --log-priority=". +<% if $udev_log { -%> +udev_log=<%= $udev_log %> +<% } -%> +<% if $udev_children_max { -%> +children_max=<%= $udev_children_max %> +<% } -%> +<% if $udev_exec_delay { -%> +exec_delay=<%= $udev_exec_delay %> +<% } -%> +<% if $udev_event_timeout { -%> +event_timeout=<%= $udev_event_timeout %> +<% } -%> +<% if $udev_resolve_names { -%> +resolve_names=<%= $udev_resolve_names %> +<% } -%> +<% if $udev_timeout_signal { -%> +timeout_signal=<%= $udev_timeout_signal %> +<% } -%> diff --git a/templates/udev_rule.epp b/templates/udev_rule.epp new file mode 100644 index 0000000..822c025 --- /dev/null +++ b/templates/udev_rule.epp @@ -0,0 +1,5 @@ +<%- | Array $rules | -%> +# This file managed by Puppet - DO NOT EDIT +<% $rules.each | $rule | { -%> +<%= $rule %> +<% } -%>