From f9d42e5e77dc24fd108941accdb41e826292f23e Mon Sep 17 00:00:00 2001 From: Steve Traylen Date: Tue, 26 Nov 2024 16:16:34 +0100 Subject: [PATCH] Support management of Swap unit files Let `.swap` unit files be managed with `systemd::manage_dropin` and `systemd::manage_unit`. For example: ```puppet systemd::manage_unit{'swapfile.swap': ensure => 'present, active => true, enable => true, unit_entry => { 'Description' => 'Enable a swapfile at /swapfile', } swap_entry => { 'What' => '/swapfile', } install_entry => { 'WantedBy' => 'multi-user.target', }, } ``` * https://www.freedesktop.org/software/systemd/man/latest/systemd.swap.html --- REFERENCE.md | 74 +++++++++++++++++++++ manifests/manage_dropin.pp | 7 ++ manifests/manage_unit.pp | 41 ++++++++++++ spec/defines/manage_dropin_spec.rb | 20 ++++++ spec/defines/manage_unit_spec.rb | 29 ++++++++ spec/type_aliases/systemd_unit_swap_spec.rb | 28 ++++++++ templates/unit_file.epp | 2 + types/unit/swap.pp | 11 +++ 8 files changed, 212 insertions(+) create mode 100644 spec/type_aliases/systemd_unit_swap_spec.rb create mode 100644 types/unit/swap.pp diff --git a/REFERENCE.md b/REFERENCE.md index 43cf9f0f..89841e9b 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -87,6 +87,7 @@ * [`Systemd::Unit::Service::Exec`](#Systemd--Unit--Service--Exec): Possible strings for ExecStart, ExecStartPrep, ... * [`Systemd::Unit::Slice`](#Systemd--Unit--Slice): Possible keys for the [Slice] section of a unit file * [`Systemd::Unit::Socket`](#Systemd--Unit--Socket): Possible keys for the [Socket] section of a unit file +* [`Systemd::Unit::Swap`](#Systemd--Unit--Swap): Possible keys for the [Swap] section of a unit file * [`Systemd::Unit::Timer`](#Systemd--Unit--Timer): Possible keys for the [Timer] section of a unit file * [`Systemd::Unit::Timespan`](#Systemd--Unit--Timespan): Timer specification for systemd time spans, e.g. timers. * [`Systemd::Unit::Unit`](#Systemd--Unit--Unit): Possible keys for the [Unit] section of a unit file @@ -1125,6 +1126,7 @@ The following parameters are available in the `systemd::manage_dropin` defined t * [`path_entry`](#-systemd--manage_dropin--path_entry) * [`socket_entry`](#-systemd--manage_dropin--socket_entry) * [`mount_entry`](#-systemd--manage_dropin--mount_entry) +* [`swap_entry`](#-systemd--manage_dropin--swap_entry) ##### `unit` @@ -1276,6 +1278,14 @@ key value pairs for the [Mount] section of the unit file Default value: `undef` +##### `swap_entry` + +Data type: `Optional[Systemd::Unit::Swap]` + +key value pairs for the [Swap] section of the unit file + +Default value: `undef` + ### `systemd::manage_unit` Generate unit file from template @@ -1377,6 +1387,43 @@ systemd::manage_dropin { 'tmpfs-db.conf': } ``` +##### Create and Mound a Swap File + +```puppet + +systemd::manage_unit{'swapfile.swap': + ensure => present, + enable => true, + active => true, + unit_entry => { + 'Description' => 'Mount /swapfile as a swap file', + 'After' => 'mkswap.service', + 'Requires' => 'mkswap.service', + }, + swap_entry => { + 'What' => '/swapfile', + }, + install_entry => { + 'WantedBy' => 'multi-user.target', + }, + require => Systemd::Manage_unit['mkswap.service'], +} +systemd::manage_unit{'mkswap.service': + ensure => present, + unit_entry => { + 'Description' => 'Format a swapfile at /swapfile', + 'ConditionPathExists' => '!/swapfile', + }, + service_entry => { + 'type' => 'oneshot', + 'ExecStart' => [ + '/usr/bin/dd if=/dev/zero of=/swapfile bs=1024 count=1000', + '/usr/sbin/mkswap /swapfile', + ], + }, +} +``` + ##### Remove a unit file ```puppet @@ -1411,6 +1458,7 @@ The following parameters are available in the `systemd::manage_unit` defined typ * [`path_entry`](#-systemd--manage_unit--path_entry) * [`socket_entry`](#-systemd--manage_unit--socket_entry) * [`mount_entry`](#-systemd--manage_unit--mount_entry) +* [`swap_entry`](#-systemd--manage_unit--swap_entry) ##### `name` @@ -1586,6 +1634,14 @@ kev value pairs for [Mount] section of the unit file. Default value: `undef` +##### `swap_entry` + +Data type: `Optional[Systemd::Unit::Swap]` + +kev value pairs for [Swap] section of the unit file. + +Default value: `undef` + ### `systemd::modules_load` Creates a modules-load.d drop file @@ -3435,6 +3491,24 @@ Struct[{ }] ``` +### `Systemd::Unit::Swap` + +Possible keys for the [Swap] section of a unit file + +* **See also** + * https://www.freedesktop.org/software/systemd/man/latest/systemd.swap.html + +Alias of + +```puppet +Struct[{ + Optional['What'] => String[1], + Optional['Options'] => String[1], + Optional['Priority'] => Integer, + Optional['TimeoutSec'] => Variant[Integer[0],String[0]] + }] +``` + ### `Systemd::Unit::Timer` Possible keys for the [Timer] section of a unit file diff --git a/manifests/manage_dropin.pp b/manifests/manage_dropin.pp index 8df9da9c..a5f7d0bb 100644 --- a/manifests/manage_dropin.pp +++ b/manifests/manage_dropin.pp @@ -89,6 +89,7 @@ # @param path_entry key value pairs for [Path] section of the unit file # @param socket_entry key value pairs for the [Socket] section of the unit file # @param mount_entry key value pairs for the [Mount] section of the unit file +# @param swap_entry key value pairs for the [Swap] section of the unit file # define systemd::manage_dropin ( Systemd::Unit $unit, @@ -110,6 +111,7 @@ Optional[Systemd::Unit::Path] $path_entry = undef, Optional[Systemd::Unit::Socket] $socket_entry = undef, Optional[Systemd::Unit::Mount] $mount_entry = undef, + Optional[Systemd::Unit::Swap] $swap_entry = undef, ) { if $timer_entry and $unit !~ Pattern['^[^/]+\.timer'] { fail("Systemd::Manage_dropin[${name}]: for unit ${unit} timer_entry is only valid for timer units") @@ -131,6 +133,10 @@ fail("Systemd::Manage_dropin[${name}]: for unit ${unit} mount_entry is only valid for mount units") } + if $swap_entry and $unit !~ Pattern['^[^/]+\.swap'] { + fail("Systemd::Manage_dropin[${name}]: for unit ${unit} mount_entry is only valid for swap units") + } + systemd::dropin_file { $name: ensure => $ensure, filename => $filename, @@ -152,6 +158,7 @@ 'path_entry' => $path_entry, 'socket_entry' => $socket_entry, 'mount_entry' => $mount_entry, + 'swap_entry' => $swap_entry, }), } } diff --git a/manifests/manage_unit.pp b/manifests/manage_unit.pp index c406a5ca..b9177f37 100644 --- a/manifests/manage_unit.pp +++ b/manifests/manage_unit.pp @@ -84,6 +84,40 @@ # }, # } # +# @example Create and Mound a Swap File +# +# systemd::manage_unit{'swapfile.swap': +# ensure => present, +# enable => true, +# active => true, +# unit_entry => { +# 'Description' => 'Mount /swapfile as a swap file', +# 'After' => 'mkswap.service', +# 'Requires' => 'mkswap.service', +# }, +# swap_entry => { +# 'What' => '/swapfile', +# }, +# install_entry => { +# 'WantedBy' => 'multi-user.target', +# }, +# require => Systemd::Manage_unit['mkswap.service'], +# } +# systemd::manage_unit{'mkswap.service': +# ensure => present, +# unit_entry => { +# 'Description' => 'Format a swapfile at /swapfile', +# 'ConditionPathExists' => '!/swapfile', +# }, +# service_entry => { +# 'type' => 'oneshot', +# 'ExecStart' => [ +# '/usr/bin/dd if=/dev/zero of=/swapfile bs=1024 count=1000', +# '/usr/sbin/mkswap /swapfile', +# ], +# }, +# } +# # @example Remove a unit file # systemd::manage_unit { 'my.service': # ensure => 'absent', @@ -118,6 +152,7 @@ # @param path_entry key value pairs for [Path] section of the unit file. # @param socket_entry kev value paors for [Socket] section of the unit file. # @param mount_entry kev value pairs for [Mount] section of the unit file. +# @param swap_entry kev value pairs for [Swap] section of the unit file. # define systemd::manage_unit ( Enum['present', 'absent'] $ensure = 'present', @@ -141,6 +176,7 @@ Optional[Systemd::Unit::Path] $path_entry = undef, Optional[Systemd::Unit::Socket] $socket_entry = undef, Optional[Systemd::Unit::Mount] $mount_entry = undef, + Optional[Systemd::Unit::Swap] $swap_entry = undef, ) { assert_type(Systemd::Unit, $name) @@ -164,6 +200,10 @@ fail("Systemd::Manage_unit[${name}]: mount_entry is only valid for mount units") } + if $swap_entry and $name !~ Pattern['^[^/]+\.swap'] { + fail("Systemd::Manage_unit[${name}]: swap_entry is only valid for swap units") + } + if $ensure != 'absent' and $name =~ Pattern['^[^/]+\.service'] and !$service_entry { fail("Systemd::Manage_unit[${name}]: service_entry is required for service units") } @@ -190,6 +230,7 @@ 'path_entry' => $path_entry, 'socket_entry' => $socket_entry, 'mount_entry' => $mount_entry, + 'swap_entry' => $swap_entry, }), } } diff --git a/spec/defines/manage_dropin_spec.rb b/spec/defines/manage_dropin_spec.rb index 08ff4791..a6d5c5bc 100644 --- a/spec/defines/manage_dropin_spec.rb +++ b/spec/defines/manage_dropin_spec.rb @@ -134,6 +134,26 @@ end end + context 'on a swap unit' do + let(:params) do + { + unit: 'file.swap', + swap_entry: { + 'Priority' => 10, + } + } + end + + it { is_expected.to compile.with_all_deps } + + it { + is_expected.to contain_systemd__dropin_file('foobar.conf'). + with_unit('file.swap'). + with_content(%r{^\[Swap\]$}). + with_content(%r{^Priority=10$}) + } + end + context 'on a timer' do let(:params) do { diff --git a/spec/defines/manage_unit_spec.rb b/spec/defines/manage_unit_spec.rb index 0e283030..003e0acc 100644 --- a/spec/defines/manage_unit_spec.rb +++ b/spec/defines/manage_unit_spec.rb @@ -127,6 +127,35 @@ } end + context 'on a swap' do + let(:title) { 'file.swap' } + + let(:params) do + { + unit_entry: { + Description: 'Add swap from a file', + }, + swap_entry: { + 'What' => '/file', + 'TimeoutSec' => 100, + 'Options' => 'trim', + 'Priority' => 10, + }, + } + end + + it { is_expected.to compile.with_all_deps } + + it { + is_expected.to contain_systemd__unit_file('file.swap'). + with_content(%r{^\[Swap\]$}). + with_content(%r{^What=/file$}). + with_content(%r{^TimeoutSec=100$}). + with_content(%r{^Options=trim$}). + with_content(%r{^Priority=10$}) + } + end + context 'on a timer' do let(:title) { 'winter.timer' } diff --git a/spec/type_aliases/systemd_unit_swap_spec.rb b/spec/type_aliases/systemd_unit_swap_spec.rb new file mode 100644 index 00000000..626d1a52 --- /dev/null +++ b/spec/type_aliases/systemd_unit_swap_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Systemd::Unit::Swap' do + context 'with a key of What can have thing to mount' do + it { is_expected.to allow_value({ 'What' => '/tmpfs' }) } + it { is_expected.to allow_value({ 'What' => '/dev/vda1' }) } + end + + context 'with a key of Options' do + it { is_expected.to allow_value({ 'Options' => 'trim' }) } + end + + context 'with a key of Priority' do + it { is_expected.to allow_value({ 'Priority' => 10 }) } + end + + context 'with a key of TimeoutSec can have a mode of' do + it { is_expected.to allow_value({ 'TimeoutSec' => '100' }) } + it { is_expected.to allow_value({ 'TimeoutSec' => '5min 20s' }) } + it { is_expected.to allow_value({ 'TimeoutSec' => '' }) } + end + + context 'with a key of Where' do + it { is_expected.not_to allow_value({ 'Where' => '/mnt/foo' }) } + end +end diff --git a/templates/unit_file.epp b/templates/unit_file.epp index 59cfa7ec..2d3a7803 100644 --- a/templates/unit_file.epp +++ b/templates/unit_file.epp @@ -7,6 +7,7 @@ Optional[Hash] $path_entry, Optional[Hash] $socket_entry, Optional[Hash] $mount_entry, + Optional[Hash] $swap_entry, | -%> <%- @@ -22,6 +23,7 @@ 'Socket', 'Install', 'Mount', + 'Swap', ] # Directives which are pair of items to be expressed as a space seperated pair. diff --git a/types/unit/swap.pp b/types/unit/swap.pp new file mode 100644 index 00000000..03106911 --- /dev/null +++ b/types/unit/swap.pp @@ -0,0 +1,11 @@ +# @summary Possible keys for the [Swap] section of a unit file +# @see https://www.freedesktop.org/software/systemd/man/latest/systemd.swap.html +# +type Systemd::Unit::Swap = Struct[ + { + Optional['What'] => String[1], + Optional['Options'] => String[1], + Optional['Priority'] => Integer, + Optional['TimeoutSec'] => Variant[Integer[0],String[0]] + } +]