diff --git a/.msync.yml b/.msync.yml index 43966c2..ab186de 100644 --- a/.msync.yml +++ b/.msync.yml @@ -1,5 +1,5 @@ --- # Managed by modulesync - DO NOT EDIT # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ -modulesync_config_version: '4.2.0' +modulesync_config_version: '5.0.1' diff --git a/Gemfile b/Gemfile index e43173e..2b731b9 100644 --- a/Gemfile +++ b/Gemfile @@ -1,35 +1,35 @@ # Managed by modulesync - DO NOT EDIT # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ source ENV['GEM_SOURCE'] || "https://rubygems.org" group :test do - gem 'voxpupuli-test', '~> 2.5', :require => false + gem 'voxpupuli-test', '~> 4.0', :require => false gem 'coveralls', :require => false gem 'simplecov-console', :require => false gem 'puppet_metadata', '~> 1.0', :require => false gem 'puppet-lint-param-docs', :require => false end group :development do gem 'guard-rake', :require => false gem 'overcommit', '>= 0.39.1', :require => false end group :system_tests do gem 'voxpupuli-acceptance', '~> 1.0', :require => false end group :release do gem 'github_changelog_generator', '>= 1.16.1', :require => false if RUBY_VERSION >= '2.5' - gem 'voxpupuli-release', '>= 1.0.2', :require => false + gem 'voxpupuli-release', '>= 1.2.0', :require => false gem 'puppet-strings', '>= 2.2', :require => false end gem 'rake', :require => false gem 'facter', ENV['FACTER_GEM_VERSION'], :require => false, :groups => [:test] puppetversion = ENV['PUPPET_VERSION'] || '>= 6.0' gem 'puppet', puppetversion, :require => false, :groups => [:test] # vim: syntax=ruby diff --git a/lib/facter/systemd.rb b/lib/facter/systemd.rb index cef9a77..d6a2db3 100644 --- a/lib/facter/systemd.rb +++ b/lib/facter/systemd.rb @@ -1,61 +1,63 @@ +# frozen_string_literal: true + # Fact: systemd # # Purpose: # Determine whether systemd is the init system on the node # # Resolution: # Check if the service_provider fact is systemd # # Caveats: # If you override the service provider then it will return false, even if the # underlying system still is systemd. # # Fact: systemd_version # # Purpose: # Determine the version of systemd installed # # Resolution: # Check the output of systemctl --version # # Caveats: # # Fact: systemd_internal_services # # Purpose: # List all systemd internal real services + their state # # Resolution: # Check the output of systemctl --list-unit-files systemd-* and parse it into # a hash with the status # # Caveats: # Facter.add(:systemd) do confine kernel: :linux setcode do Facter.value(:service_provider) == 'systemd' end end Facter.add(:systemd_version) do confine systemd: true setcode do Facter::Util::Resolution.exec("systemctl --version | awk '/systemd/{ print $2 }'") end end Facter.add(:systemd_internal_services) do confine systemd: true setcode do command_output = Facter::Util::Resolution.exec( 'systemctl list-unit-files --no-legend --no-pager "systemd-*" -t service --state=enabled,disabled,enabled-runtime,indirect' ) lines = command_output.lines.lazy.map { |line| line.split(%r{\s+}) } lines.each_with_object({}) do |(service, status, *), result| result[service] = status end end end diff --git a/lib/puppet/provider/loginctl_user/ruby.rb b/lib/puppet/provider/loginctl_user/ruby.rb index 588247d..7c5789a 100644 --- a/lib/puppet/provider/loginctl_user/ruby.rb +++ b/lib/puppet/provider/loginctl_user/ruby.rb @@ -1,38 +1,40 @@ +# frozen_string_literal: true + # @summary custom provider to manage systemd user sessions/linger # @see https://www.freedesktop.org/software/systemd/man/loginctl.html # @see https://wiki.archlinux.org/title/Systemd/User Puppet::Type.type(:loginctl_user).provide(:ruby) do desc 'custom provider to manage systemd user sessions/linger' commands loginctl: 'loginctl' def self.instances users = loginctl('list-users', '--no-legend').split("\n").map { |l| l.split[1] } loginctl('show-user', '-p', 'Name', '-p', 'Linger', *users).split("\n\n").map do |u| user = u.split("\n").map { |f| f.split('=') }.to_h linger = if user['Linger'] == 'yes' 'enabled' else 'disabled' end new(name: user['Name'], linger: linger) end end def self.prefetch(resources) instances.each do |prov| resources[prov.name].provider = prov if resources[prov.name] end end mk_resource_methods def linger=(value) case value when :enabled loginctl('enable-linger', resource[:name]) when :disabled loginctl('disable-linger', resource[:name]) end end end diff --git a/lib/puppet/type/loginctl_user.rb b/lib/puppet/type/loginctl_user.rb index 5b9ffd4..54aeb0f 100644 --- a/lib/puppet/type/loginctl_user.rb +++ b/lib/puppet/type/loginctl_user.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + require 'digest/md5' Puppet::Type.newtype(:loginctl_user) do newparam(:name, namevar: true) do desc 'An arbitrary name used as the identity of the resource.' end newproperty(:linger) do desc 'Whether linger is enabled for the user.' newvalues :enabled, :disabled defaultto :disabled end end diff --git a/spec/classes/init_spec.rb b/spec/classes/init_spec.rb index 620cb44..ee2cf86 100644 --- a/spec/classes/init_spec.rb +++ b/spec/classes/init_spec.rb @@ -1,518 +1,545 @@ +# frozen_string_literal: true + 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 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) } it { is_expected.not_to contain_file('/etc/systemd/network') } 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( + expect(subject).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( + expect(subject).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 passing networks' do let :params do { networks: { 'uplink.network' => { 'content' => 'foo' }, 'uplink.netdev' => { 'content' => 'bar' }, }, } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_systemd__network('uplink.network').with_content('foo') } it { is_expected.to contain_systemd__network('uplink.netdev').with_content('bar') } it { is_expected.to contain_file('/etc/systemd/network/uplink.network') } it { is_expected.to contain_file('/etc/systemd/network/uplink.netdev') } it { is_expected.to have_systemd__network_resource_count(2) } end + context 'when passing timers' do let :params do { timers: { 'first.timer' => { 'timer_content' => 'foo' }, 'second.timer' => { 'timer_content' => 'bar' }, }, } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_systemd__timer('first.timer').with_timer_content('foo') } it { is_expected.to contain_systemd__timer('second.timer').with_timer_content('bar') } it { is_expected.to contain_systemd__unit_file('first.timer').with_content('foo') } it { is_expected.to contain_systemd__unit_file('second.timer').with_content('bar') } it { is_expected.to contain_file('/etc/systemd/system/first.timer') } it { is_expected.to contain_file('/etc/systemd/system/second.timer') } it { is_expected.to have_systemd__timer_resource_count(2) } it { is_expected.to have_systemd__unit_file_resource_count(2) } end + context 'when passing tmpfiles' do let :params do { tmpfiles: { 'first_tmpfile.conf' => { 'content' => 'foo' }, 'second_tmpfile.conf' => { 'content' => 'bar' }, }, } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_systemd__tmpfile('first_tmpfile.conf').with_content('foo') } it { is_expected.to contain_systemd__tmpfile('second_tmpfile.conf').with_content('bar') } it { is_expected.to contain_file('/etc/tmpfiles.d/first_tmpfile.conf') } it { is_expected.to contain_file('/etc/tmpfiles.d/second_tmpfile.conf') } it { is_expected.to have_systemd__tmpfile_resource_count(2) } end + context 'when passing unit_files' do let :params do { unit_files: { 'first.service' => { 'content' => 'foo' }, 'second.service' => { 'content' => 'bar' }, }, } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_systemd__unit_file('first.service').with_content('foo') } it { is_expected.to contain_systemd__unit_file('second.service').with_content('bar') } it { is_expected.to contain_file('/etc/systemd/system/first.service') } it { is_expected.to contain_file('/etc/systemd/system/second.service') } it { is_expected.to have_systemd__unit_file_resource_count(2) } 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' + when 'Archlinux', 'Gentoo' accounting = %w[DefaultCPUAccounting DefaultIOAccounting DefaultIPAccounting DefaultBlockIOAccounting DefaultMemoryAccounting DefaultTasksAccounting] when 'Debian' accounting = %w[DefaultCPUAccounting DefaultBlockIOAccounting DefaultMemoryAccounting] - when 'Gentoo' - accounting = %w[DefaultCPUAccounting DefaultIOAccounting DefaultIPAccounting DefaultBlockIOAccounting DefaultMemoryAccounting DefaultTasksAccounting] - when 'RedHat' - accounting = %w[DefaultCPUAccounting DefaultBlockIOAccounting DefaultMemoryAccounting DefaultTasksAccounting] - when 'Suse' + when 'RedHat', 'Suse' accounting = %w[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', + 'Storage' => 'auto', 'MaxRetentionSec' => '5day', - 'MaxLevelStore' => { + 'MaxLevelStore' => { 'ensure' => 'absent', }, }, } end it { is_expected.to compile.with_all_deps } + it { - is_expected.to contain_service('systemd-journald').with( + expect(subject).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( + expect(subject).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( + expect(subject).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( + expect(subject).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'). + expect(subject).to contain_service('systemd-udevd'). with(enable: true, ensure: 'running') } + it { - is_expected.to contain_file('/etc/udev/udev.conf'). + expect(subject).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'). + expect(subject).to contain_service('systemd-udevd'). with(enable: true, ensure: 'running') } + it { - is_expected.to contain_file('/etc/udev/udev.conf'). + expect(subject).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'). + expect(subject).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', + 'HandleSuspendKey' => 'ignore', 'KillUserProcesses' => 'no', - 'KillExcludeUsers' => %w[a b], - 'RemoveIPC' => { + 'KillExcludeUsers' => %w[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( + expect(subject).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( + expect(subject).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( + expect(subject).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( + expect(subject).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( + expect(subject).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( + expect(subject).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', + 'unit' => 'foo.service', 'content' => '[Service]\nReadWritePaths=/', }, }, } end it { is_expected.to contain_systemd__dropin_file('my-foo.conf').with_content('[Service]\nReadWritePaths=/') } end + context 'with managed networkd directory' do let :params do { manage_networkd: true, manage_all_network_files: true } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_class('systemd::networkd') } it { is_expected.to contain_file('/etc/systemd/network').with_ensure('directory') } end end end end end diff --git a/spec/classes/tmpfiles_spec.rb b/spec/classes/tmpfiles_spec.rb index 13defae..51036f7 100644 --- a/spec/classes/tmpfiles_spec.rb +++ b/spec/classes/tmpfiles_spec.rb @@ -1,15 +1,17 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'systemd::tmpfiles' 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::tmpfiles') } it { is_expected.to contain_exec('systemd-tmpfiles').with_command('systemd-tmpfiles --create') } end end end end diff --git a/spec/defines/dropin_file_spec.rb b/spec/defines/dropin_file_spec.rb index 42292ac..7a82d7d 100644 --- a/spec/defines/dropin_file_spec.rb +++ b/spec/defines/dropin_file_spec.rb @@ -1,146 +1,149 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'systemd::dropin_file' do context 'supported operating systems' do on_supported_os.each do |os, facts| context "on #{os}" do let(:facts) { facts } let(:title) { 'test.conf' } let(:params) do { unit: 'test.service', content: 'random stuff', } end it { is_expected.to compile.with_all_deps } it { - is_expected.to create_file("/etc/systemd/system/#{params[:unit]}.d").with( + expect(subject).to create_file("/etc/systemd/system/#{params[:unit]}.d").with( ensure: 'directory', recurse: 'true', purge: 'true', selinux_ignore_defaults: false ) } it { - is_expected.to create_file("/etc/systemd/system/#{params[:unit]}.d/#{title}").with( + expect(subject).to create_file("/etc/systemd/system/#{params[:unit]}.d/#{title}").with( ensure: 'file', content: %r{#{params[:content]}}, mode: '0444', selinux_ignore_defaults: false ) } context 'notifies services' do let(:params) do super().merge(notify_service: true) end let(:filename) { "/etc/systemd/system/#{params[:unit]}.d/#{title}" } let(:pre_condition) do <<-PUPPET service { ['test', 'test.service']: } PUPPET end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_service('test').that_subscribes_to("File[#{filename}]") } it { is_expected.to contain_service('test.service').that_subscribes_to("File[#{filename}]") } context 'with overridden name' do let(:pre_condition) do <<-PUPPET service { 'myservice': name => 'test', } PUPPET end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_service('myservice').that_subscribes_to("File[#{filename}]") } end end context 'with selinux_ignore_defaults set to true' do let(:params) do super().merge(selinux_ignore_defaults: true) end it { is_expected.to create_file("/etc/systemd/system/#{params[:unit]}.d").with_selinux_ignore_defaults(true) } it { is_expected.to create_file("/etc/systemd/system/#{params[:unit]}.d/#{title}").with_selinux_ignore_defaults(true) } end context 'with a bad unit type' do let(:title) { 'test.badtype' } it { expect do - is_expected.to compile.with_all_deps + expect(subject).to compile.with_all_deps end.to raise_error(%r{expects a match for Systemd::Dropin}) } end context 'with a bad unit type containing a slash' do let(:title) { 'test/bad.conf' } it { expect do - is_expected.to compile.with_all_deps + expect(subject).to compile.with_all_deps end.to raise_error(%r{expects a match for Systemd::Dropin}) } end context 'with another drop-in file with the same filename (and content)' do let(:default_params) do { filename: 'longer-timeout.conf', content: 'random stuff', } end # Create drop-in file longer-timeout.conf for unit httpd.service let(:pre_condition) do "systemd::dropin_file { 'httpd_longer-timeout': filename => '#{default_params[:filename]}', unit => 'httpd.service', content => '#{default_params[:context]}', }" end # # Create drop-in file longer-timeout.conf for unit ftp.service let(:title) { 'ftp_longer-timeout' } let(:params) do default_params.merge(unit: 'ftp.service') end it { - is_expected.to create_file("/etc/systemd/system/#{params[:unit]}.d/#{params[:filename]}").with( + expect(subject).to create_file("/etc/systemd/system/#{params[:unit]}.d/#{params[:filename]}").with( ensure: 'file', content: %r{#{params[:content]}}, mode: '0444' ) } end + context 'with sensitve content' do let(:title) { 'sensitive.conf' } let(:params) do { unit: 'sensitive.service', content: RSpec::Puppet::RawString.new("Sensitive('TEST_CONTENT')"), } end it { - is_expected.to create_file("/etc/systemd/system/#{params[:unit]}.d/#{title}").with( + expect(subject).to create_file("/etc/systemd/system/#{params[:unit]}.d/#{title}").with( ensure: 'file', content: sensitive('TEST_CONTENT') ) } end end end end end diff --git a/spec/defines/network_spec.rb b/spec/defines/network_spec.rb index dac8219..b4f3df4 100644 --- a/spec/defines/network_spec.rb +++ b/spec/defines/network_spec.rb @@ -1,84 +1,88 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'systemd::network' do context 'supported operating systems' do on_supported_os.each do |os, facts| context "on #{os}" do # manage systemd-networkd service let :pre_condition do "class { 'systemd': manage_networkd => true, }" end let(:facts) { facts } let(:title) { 'eth0.network' } let(:params) do { content: 'random stuff', restart_service: true, } end it { is_expected.to compile.with_all_deps } it { - is_expected.to create_file("/etc/systemd/network/#{title}").with( + expect(subject).to create_file("/etc/systemd/network/#{title}").with( ensure: 'file', content: %r{#{params[:content]}}, mode: '0444' ) } it { is_expected.to create_file("/etc/systemd/network/#{title}").that_notifies('Service[systemd-networkd]') } context 'with group => systemd-network, mode => 0640 and show_diff => false' do let(:title) { 'wg0.netdev' } let(:params) do { content: 'secret string', group: 'systemd-network', mode: '0640', show_diff: false, restart_service: true, } end it { is_expected.to compile.with_all_deps } it { - is_expected.to create_file("/etc/systemd/network/#{title}").with( + expect(subject).to create_file("/etc/systemd/network/#{title}").with( ensure: 'file', content: %r{#{params[:content]}}, group: 'systemd-network', mode: '0640', show_diff: false ) } it { is_expected.to create_file("/etc/systemd/network/#{title}").that_notifies('Service[systemd-networkd]') } end + context 'without content and without source' do let :params do {} end it { is_expected.to compile.and_raise_error(%r{Either content or source must be set}) } end + context 'with content and source' do let :params do { content: 'bla', source: 'foo' } end it { is_expected.to compile.and_raise_error(%r{Either content or source must be set but not both}) } end end end end end diff --git a/spec/defines/service_limits_spec.rb b/spec/defines/service_limits_spec.rb index 3bba4e6..cbd3d5e 100644 --- a/spec/defines/service_limits_spec.rb +++ b/spec/defines/service_limits_spec.rb @@ -1,74 +1,80 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'systemd::service_limits' do context 'supported operating systems' do on_supported_os.each do |os, facts| context "on #{os}" do let(:facts) { facts } let(:title) { 'test.service' } describe 'with limits and present' do let(:params) do { limits: { - 'LimitCPU' => '10m', - 'LimitFSIZE' => 'infinity', - 'LimitDATA' => '10K', + 'LimitCPU' => '10m', + 'LimitFSIZE' => 'infinity', + 'LimitDATA' => '10K', 'LimitNOFILE' => '20:infinity', - 'LimitNICE' => '-10', + 'LimitNICE' => '-10', 'LimitRTPRIO' => 50, - 'CPUQuota' => '125%', + 'CPUQuota' => '125%', 'IODeviceWeight' => [ { '/dev/weight' => 10 }, { '/dev/weight2' => 20 }, ], 'IOReadBandwidthMax' => [ { '/bw/max' => '10K' }, ], }, } end it { is_expected.to compile.with_all_deps } + it { - is_expected.to create_file("/etc/systemd/system/#{title}.d/90-limits.conf"). + expect(subject).to create_file("/etc/systemd/system/#{title}.d/90-limits.conf"). with(ensure: 'file', mode: '0444'). with_content(%r{LimitCPU=10m}). with_content(%r{LimitFSIZE=infinity}). with_content(%r{LimitDATA=10K}). with_content(%r{LimitNOFILE=20:infinity}). with_content(%r{LimitNICE=-10}). with_content(%r{LimitRTPRIO=50}). with_content(%r{CPUQuota=125%}). with_content(%r{IODeviceWeight=/dev/weight 10}). with_content(%r{IODeviceWeight=/dev/weight2 20}). with_content(%r{IOReadBandwidthMax=/bw/max 10K}) } + it { - is_expected.to create_exec("restart #{title} because limits").with( + expect(subject).to create_exec("restart #{title} because limits").with( command: "systemctl restart #{title}", refreshonly: true ) } 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/systemd/system/#{title}.d/90-limits.conf"). + expect(subject).to create_file("/etc/systemd/system/#{title}.d/90-limits.conf"). with_ensure('absent'). that_notifies("Exec[restart #{title} because limits]") end + it do - is_expected.to create_exec("restart #{title} because limits"). + expect(subject).to create_exec("restart #{title} because limits"). with_command("systemctl restart #{title}"). with_refreshonly(true) end end end end end end diff --git a/spec/defines/timer_spec.rb b/spec/defines/timer_spec.rb index d187a51..048e43a 100644 --- a/spec/defines/timer_spec.rb +++ b/spec/defines/timer_spec.rb @@ -1,106 +1,108 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'systemd::timer' do context 'supported operating systems' do on_supported_os.each do |os, facts| context "on #{os}" do let(:facts) { facts } let(:title) { 'foobar.timer' } context('with timer_content and service_content') do let(:params) do { timer_content: "[Timer]\nOnCalendar=weekly", service_content: "[Service]\nExecStart=/bin/touch /tmp/foobar", } end it { is_expected.to compile.with_all_deps } it { - is_expected.to contain_systemd__unit_file('foobar.timer').with( + expect(subject).to contain_systemd__unit_file('foobar.timer').with( content: "[Timer]\nOnCalendar=weekly" ) } it { - is_expected.to contain_systemd__unit_file('foobar.service').with( + expect(subject).to contain_systemd__unit_file('foobar.service').with( content: "[Service]\nExecStart=/bin/touch /tmp/foobar" ) } end context('with timer_source and service_source') do let(:params) do { timer_source: 'puppet:///timer', service_source: 'puppet:///source', } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_systemd__unit_file('foobar.timer').with_source('puppet:///timer') } it { is_expected.to contain_systemd__unit_file('foobar.service').with_source('puppet:///source') } end context('with timer_source only set') do let(:params) do { timer_source: 'puppet:///timer', } end it { is_expected.to compile.with_all_deps } it { is_expected.to contain_systemd__unit_file('foobar.timer').with_source('puppet:///timer') } it { is_expected.not_to contain_systemd__unit_file('foobar.service') } end context 'with service_unit specified' do let(:params) do { timer_content: "[Timer]\nOnCalendar=weekly", service_content: "[Service]\nExecStart=/bin/touch /tmp/foobar", service_unit: 'gamma.service', } end it { is_expected.to contain_systemd__unit_file('foobar.timer').with_content("[Timer]\nOnCalendar=weekly") } it { is_expected.to contain_systemd__unit_file('gamma.service').with_content("[Service]\nExecStart=/bin/touch /tmp/foobar") } end context 'with timer activated service' do let(:params) do { active: true, enable: true, timer_content: "[Timer]\nOnCalendar=hourly", service_content: "[Service]\nExecStart=/bin/echo timer-fired", } end it { is_expected.to contain_systemd__unit_file('foobar.timer').with_content("[Timer]\nOnCalendar=hourly") } it { is_expected.to contain_systemd__unit_file('foobar.service').with_content("[Service]\nExecStart=/bin/echo timer-fired") } it { - is_expected.to create_exec('foobar.service-systemctl-daemon-reload').with( + expect(subject).to create_exec('foobar.service-systemctl-daemon-reload').with( command: 'systemctl daemon-reload', refreshonly: true ) } end context 'with a bad timer name' do let(:title) { 'foobar' } it { expect do - is_expected.to compile.with_all_deps + expect(subject).to compile.with_all_deps end.to raise_error(%r{expects a match for}) } end end end end end diff --git a/spec/defines/tmpfile_spec.rb b/spec/defines/tmpfile_spec.rb index 97f81cf..defe125 100644 --- a/spec/defines/tmpfile_spec.rb +++ b/spec/defines/tmpfile_spec.rb @@ -1,60 +1,63 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'systemd::tmpfile' do context 'supported operating systems' do on_supported_os.each do |os, facts| context "on #{os}" do let(:facts) { facts } let(:title) { 'random_tmpfile.conf' } let(:params) { { content: 'random stuff' } } it { is_expected.to compile.with_all_deps } + it { - is_expected.to create_file("/etc/tmpfiles.d/#{title}").with( + expect(subject).to create_file("/etc/tmpfiles.d/#{title}").with( ensure: 'file', content: %r{#{params[:content]}}, mode: '0444' ) } context 'with a bad tmpfile name' do let(:title) { 'test.badtype' } it { expect do - is_expected.to compile.with_all_deps + expect(subject).to compile.with_all_deps end.to raise_error(%r{expects a match for Systemd::Dropin}) } end context 'with a bad tmpfile name with slash' do let(:title) { 'test/foo.conf' } it { expect do - is_expected.to compile.with_all_deps + expect(subject).to compile.with_all_deps end.to raise_error(%r{expects a match for Systemd::Dropin}) } end context 'with a tmpfile name specified with filename' do let(:title) { 'test.badtype' } let(:params) do { filename: 'goodname.conf', content: 'random stuff', } end it { - is_expected.to create_file('/etc/tmpfiles.d/goodname.conf').with( + expect(subject).to create_file('/etc/tmpfiles.d/goodname.conf').with( ensure: 'file', content: %r{#{params[:content]}}, mode: '0444' ) } end end end end end diff --git a/spec/defines/unit_file_spec.rb b/spec/defines/unit_file_spec.rb index 24984e6..1561b38 100644 --- a/spec/defines/unit_file_spec.rb +++ b/spec/defines/unit_file_spec.rb @@ -1,111 +1,115 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'systemd::unit_file' do context 'supported operating systems' do on_supported_os.each do |os, facts| context "on #{os}" do let(:facts) { facts } let(:title) { 'test.service' } let(:params) { { content: 'random stuff' } } it { is_expected.to compile.with_all_deps } context 'with non-sensitive Content' do let(:params) { { content: 'non-sensitive Content' } } it do - is_expected.to create_file("/etc/systemd/system/#{title}"). + expect(subject).to create_file("/etc/systemd/system/#{title}"). with_ensure('file'). with_content(params[:content]). with_mode('0444') end end context 'with sensitive Content' do let(:params) { { content: sensitive('sensitive Content') } } it do resource = catalogue.resource("File[/etc/systemd/system/#{title}]") expect(resource[:content]).to eq(params[:content].unwrap) - is_expected.to contain_file("/etc/systemd/system/#{title}"). + expect(subject).to contain_file("/etc/systemd/system/#{title}"). with({ content: sensitive('sensitive Content') }) end end context 'with a bad unit type' do let(:title) { 'test.badtype' } it { is_expected.to compile.and_raise_error(%r{expects a match for Systemd::Unit}) } end context 'with a bad unit type containing a slash' do let(:title) { 'test/unit.service' } it { is_expected.to compile.and_raise_error(%r{expects a match for Systemd::Unit}) } end context 'with enable => true and active => true' do let(:params) do super().merge( enable: true, active: true ) end it { is_expected.to compile.with_all_deps } + it do - is_expected.to contain_service('test.service'). + expect(subject).to contain_service('test.service'). with_ensure(true). with_enable(true). with_provider('systemd'). that_subscribes_to("File[/etc/systemd/system/#{title}]") end end context 'ensure => absent' do let(:params) { super().merge(ensure: 'absent') } context 'with enable => true' do let(:params) { super().merge(enable: true) } it { is_expected.to compile.and_raise_error(%r{Can't ensure the unit file is absent and activate}) } end context 'with active => true' do let(:params) { super().merge(active: true) } it { is_expected.to compile.and_raise_error(%r{Can't ensure the unit file is absent and activate}) } end context 'with enable => false and active => false' do let(:params) do super().merge( enable: false, active: false ) end it { is_expected.to compile.with_all_deps } + it do - is_expected.to contain_service('test.service'). + expect(subject).to contain_service('test.service'). with_ensure(false). with_enable(false). with_provider('systemd'). that_comes_before("File[/etc/systemd/system/#{title}]") end end end context 'when using default values for enable and active' do it { - is_expected.to create_exec("#{title}-systemctl-daemon-reload").with( + expect(subject).to create_exec("#{title}-systemctl-daemon-reload").with( command: 'systemctl daemon-reload', refreshonly: true ) } end end end end end diff --git a/spec/functions/escape_spec.rb b/spec/functions/escape_spec.rb index f1d49d3..2be13e6 100644 --- a/spec/functions/escape_spec.rb +++ b/spec/functions/escape_spec.rb @@ -1,19 +1,22 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'systemd::escape' do context 'with path false' do it { is_expected.to run.with_params('foo', false).and_return('foo') } it { is_expected.to run.with_params('foo/bar/.', false).and_return('foo-bar-.') } it { is_expected.to run.with_params('/foo/bar/', false).and_return('-foo-bar-') } it { is_expected.to run.with_params('//foo//bar//', false).and_return('--foo--bar--') } it { is_expected.to run.with_params('//foo:bar,foo_bar.//', false).and_return('--foo:bar\x2cfoo_bar.--') } it { is_expected.to run.with_params('.foo', false).and_return('\x2efoo') } end + context 'with path true' do it { is_expected.to run.with_params('foo', true).and_return('foo') } it { is_expected.to run.with_params('foo/bar/.', true).and_raise_error(%r{ path can not end}) } it { is_expected.to run.with_params('/foo/bar/', true).and_return('foo-bar') } it { is_expected.to run.with_params('//foo//bar//', true).and_return('foo-bar') } it { is_expected.to run.with_params('//foo:bar,foo_bar.//', true).and_return('foo:bar\x2cfoo_bar.') } it { is_expected.to run.with_params('.foo', true).and_return('\x2efoo') } end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index fb5f0cb..4d617f3 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,17 +1,17 @@ +# frozen_string_literal: true + # Managed by modulesync - DO NOT EDIT # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ # puppetlabs_spec_helper will set up coverage if the env variable is set. # We want to do this if lib exists and it hasn't been explicitly set. -ENV['COVERAGE'] ||= 'yes' if Dir.exist?(File.expand_path('../../lib', __FILE__)) +ENV['COVERAGE'] ||= 'yes' if Dir.exist?(File.expand_path('../lib', __dir__)) require 'voxpupuli/test/spec_helper' if File.exist?(File.join(__dir__, 'default_module_facts.yml')) facts = YAML.safe_load(File.read(File.join(__dir__, 'default_module_facts.yml'))) - if facts - facts.each do |name, value| - add_custom_fact name.to_sym, value - end + facts&.each do |name, value| + add_custom_fact name.to_sym, value end end diff --git a/spec/unit/facter/systemd_spec.rb b/spec/unit/facter/systemd_spec.rb index 4e40230..75b3843 100644 --- a/spec/unit/facter/systemd_spec.rb +++ b/spec/unit/facter/systemd_spec.rb @@ -1,36 +1,39 @@ +# frozen_string_literal: true + require 'spec_helper' describe Facter.fact(:systemd) do before { Facter.clear } + after { Facter.clear } describe 'systemd' do context 'returns true when systemd present' do before do allow(Facter.fact(:kernel)).to receive(:value).and_return(:linux) Facter.add(:service_provider) { setcode { 'systemd' } } end it { expect(Facter.value(:service_provider)).to eq('systemd') } it { expect(Facter.value(:systemd)).to be true } end context 'returns false when systemd not present' do before do allow(Facter.fact(:kernel)).to receive(:value).and_return(:linux) Facter.add(:service_provider) { setcode { 'redhat' } } end it { expect(Facter.value(:service_provider)).to eq('redhat') } it { expect(Facter.value(:systemd)).to be false } end context 'returns nil when kernel is not linux' do before do allow(Facter.fact(:kernel)).to receive(:value).and_return(:windows) end it { expect(Facter.value(:systemd)).to be_nil } end end end diff --git a/spec/unit/facter/systemd_version_spec.rb b/spec/unit/facter/systemd_version_spec.rb index 2b3845c..ba8e028 100644 --- a/spec/unit/facter/systemd_version_spec.rb +++ b/spec/unit/facter/systemd_version_spec.rb @@ -1,32 +1,41 @@ +# frozen_string_literal: true + require 'spec_helper' describe Facter.fact(:systemd_version) do before do Facter.clear end describe 'systemd_version' do context 'returns version when systemd fact present' do before do allow(Facter.fact(:systemd)).to receive(:value).and_return(true) end + let(:facts) { { systemd: true } } + # rubocop:disable RSpec/StubbedMock + # rubocop:disable RSpec/MessageSpies it do - expect(Facter::Util::Resolution).to receive(:exec).with("systemctl --version | awk '/systemd/{ print $2 }'").and_return('229') # rubocop:disable RSpec/MessageSpies + expect(Facter::Util::Resolution).to receive(:exec).with("systemctl --version | awk '/systemd/{ print $2 }'").and_return('229') expect(Facter.value(:systemd_version)).to eq('229') end end + context 'returns nil when systemd fact not present' do before do allow(Facter.fact(:systemd)).to receive(:value).and_return(false) end + let(:facts) { { systemd: false } } it do - expect(Facter::Util::Resolution).not_to receive(:exec).with("systemctl --version | awk '/systemd/{ print $2 }'") # rubocop:disable RSpec/MessageSpies + expect(Facter::Util::Resolution).not_to receive(:exec).with("systemctl --version | awk '/systemd/{ print $2 }'") expect(Facter.value(:systemd_version)).to eq(nil) end + # rubocop:enable RSpec/StubbedMock + # rubocop:enable RSpec/MessageSpies end end end diff --git a/spec/unit/puppet/provider/loginctl_user/ruby_spec.rb b/spec/unit/puppet/provider/loginctl_user/ruby_spec.rb index b3a7db1..3cd1de6 100644 --- a/spec/unit/puppet/provider/loginctl_user/ruby_spec.rb +++ b/spec/unit/puppet/provider/loginctl_user/ruby_spec.rb @@ -1,39 +1,44 @@ +# frozen_string_literal: true + require 'spec_helper' require 'puppet' provider_class = Puppet::Type.type(:loginctl_user).provider(:ruby) describe provider_class do let(:common_params) do { title: 'foo', linger: 'enabled', } end + # rubocop:disable RSpec/StubbedMock + # rubocop:disable RSpec/MessageSpies context 'when listing instances' do it 'finds all entries' do - expect(provider_class).to receive(:loginctl). # rubocop:disable RSpec/MessageSpies + expect(provider_class).to receive(:loginctl). with('list-users', '--no-legend'). and_return("0 root\n42 foo\n314 bar\n") - expect(provider_class).to receive(:loginctl). # rubocop:disable RSpec/MessageSpies + expect(provider_class).to receive(:loginctl). with('show-user', '-p', 'Name', '-p', 'Linger', 'root', 'foo', 'bar'). and_return("Name=root\nLinger=no\n\nName=foo\nLinger=yes\n\nName=bar\nLinger=no\n") - inst = provider_class.instances.map do |p| - end + inst = provider_class.instances.map! expect(inst.size).to eq(3) end end it 'enables linger' do resource = Puppet::Type.type(:loginctl_user).new(common_params) - expect(provider_class).to receive(:loginctl).with('enable-linger', 'foo') # rubocop:disable RSpec/MessageSpies + expect(provider_class).to receive(:loginctl).with('enable-linger', 'foo') resource.provider.linger = :enabled end it 'disables linger' do resource = Puppet::Type.type(:loginctl_user).new(common_params) - expect(provider_class).to receive(:loginctl).with('disable-linger', 'foo') # rubocop:disable RSpec/MessageSpies + expect(provider_class).to receive(:loginctl).with('disable-linger', 'foo') resource.provider.linger = :disabled end + # rubocop:enable RSpec/StubbedMock + # rubocop:enable RSpec/MessageSpies end diff --git a/spec/unit/puppet/type/loginctl_user_spec.rb b/spec/unit/puppet/type/loginctl_user_spec.rb index af0f34f..179fc9f 100644 --- a/spec/unit/puppet/type/loginctl_user_spec.rb +++ b/spec/unit/puppet/type/loginctl_user_spec.rb @@ -1,23 +1,25 @@ +# frozen_string_literal: true + require 'spec_helper' loginctl_user = Puppet::Type.type(:loginctl_user) describe loginctl_user do let(:common_params) do { title: 'foo', } end it 'disables linger by default' do resource = Puppet::Type.type(:loginctl_user).new(common_params) expect(resource).not_to be_nil expect(resource.parameters[:linger].value).to eq(:disabled) end it 'enables linger when requested' do resource = Puppet::Type.type(:loginctl_user).new(common_params.merge(linger: 'enabled')) expect(resource).not_to be_nil expect(resource.parameters[:linger].value).to eq(:enabled) end end