From cae855df867869ced9a7f8d235480ef286701d9b Mon Sep 17 00:00:00 2001 From: Steve Traylen Date: Tue, 27 Feb 2024 22:15:06 +0100 Subject: [PATCH] New podman::quadlet type for quadlet units Assuming at least podman 4.4.0 the new type `podman::quadlet` will create a quadlet unit files for a container, volume or pod which will in turn generate a service unit for managing the container, pod or volume. Boot root and rootless containers are supported. The included examples are operational however. Note that pod quadlets are only supported with podman 5. --- .fixtures.yml | 1 + README.md | 30 ++ REFERENCE.md | 639 ++++++++++++++++------- manifests/quadlet.pp | 150 ++++++ manifests/rootless.pp | 14 +- metadata.json | 4 + spec/defines/quadlet_spec.rb | 191 +++++++ spec/defines/rootless_spec.rb | 34 ++ spec/type_aliases/quadlet_name_spec.rb | 27 + spec/type_aliases/unit_container_spec.rb | 12 + spec/type_aliases/unit_pod_spec.rb | 7 + spec/type_aliases/unit_volume_spec.rb | 7 + templates/quadlet_file.epp | 42 ++ types/quadlet_name.pp | 3 + types/unit/container.pp | 64 +++ types/unit/pod.pp | 11 + types/unit/volume.pp | 17 + 17 files changed, 1068 insertions(+), 185 deletions(-) create mode 100644 manifests/quadlet.pp create mode 100644 spec/defines/quadlet_spec.rb create mode 100644 spec/type_aliases/quadlet_name_spec.rb create mode 100644 spec/type_aliases/unit_container_spec.rb create mode 100644 spec/type_aliases/unit_pod_spec.rb create mode 100644 spec/type_aliases/unit_volume_spec.rb create mode 100644 templates/quadlet_file.epp create mode 100644 types/quadlet_name.pp create mode 100644 types/unit/container.pp create mode 100644 types/unit/pod.pp create mode 100644 types/unit/volume.pp diff --git a/.fixtures.yml b/.fixtures.yml index 93ec5df..8623ad7 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -3,6 +3,7 @@ --- fixtures: forge_modules: + systemd: "puppet/systemd" stdlib: "puppetlabs/stdlib" concat: "puppetlabs/concat" selinux_core: "puppetlabs/selinux_core" diff --git a/README.md b/README.md index d778ea8..384a381 100644 --- a/README.md +++ b/README.md @@ -189,6 +189,36 @@ Several additional examples are in a separate [github project](https://github.co a Traefik container configuration that enables SSL termination and proxy access to other containers running on the host, with a dynamic configuration directory enabling updates to proxy rules as new containers are added and/or removed. +## Creating Containers,Volumes, Pods, ... with Quadlet Unit Files + +Container unit files can be created and managed, for example: + +```puppet +podman::quadlet{'centos.container': + ensure => present, + unit_entry => { + 'Description' => 'Trivial Container that will be very lazy', + }, + service_entry => { + 'TimeoutStartSec' => '900', + }, + container_entry => { + 'Image' => 'quay.io/centos/centos:latest', + 'Exec' => 'sh -c "sleep inf' + }, + install_entry => { + 'WantedBy' => 'default.target' + }, + active => true, +} + +``` + +Will create a service `centos.service` that is then started and enabled for boot. + +Similarly quadlets for volumes, pods, ... can be created in a similar manner + + ## Limitations The module was written and tested with RedHat/CentOS, but should work with any distribution that uses systemd and includes diff --git a/REFERENCE.md b/REFERENCE.md index c0acda8..83084e2 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -18,15 +18,23 @@ ### Defined types -* [`podman::container`](#podmancontainer): manage podman container and register as a systemd service -* [`podman::image`](#podmanimage): pull or remove container images -* [`podman::network`](#podmannetwork): Create a podman network with defined flags -* [`podman::pod`](#podmanpod): Create a podman pod with defined flags -* [`podman::rootless`](#podmanrootless): Enable a given user to run rootless podman containers as a systemd user service. -* [`podman::secret`](#podmansecret): Manage a podman secret. Create and remove secrets, it cannot replace. -* [`podman::subgid`](#podmansubgid): Define an entry in the `/etc/subgid` file. -* [`podman::subuid`](#podmansubuid): Manage entries in `/etc/subuid` -* [`podman::volume`](#podmanvolume): Create a podman volume with defined flags +* [`podman::container`](#podman--container): manage podman container and register as a systemd service +* [`podman::image`](#podman--image): pull or remove container images +* [`podman::network`](#podman--network): Create a podman network with defined flags +* [`podman::pod`](#podman--pod): Create a podman pod with defined flags +* [`podman::quadlet`](#podman--quadlet): Generate and manage podman quadlet definitions (podman > 4.4.0) +* [`podman::rootless`](#podman--rootless): Enable a given user to run rootless podman containers as a systemd user service. +* [`podman::secret`](#podman--secret): Manage a podman secret. Create and remove secrets, it cannot replace. +* [`podman::subgid`](#podman--subgid): Define an entry in the `/etc/subgid` file. +* [`podman::subuid`](#podman--subuid): Manage entries in `/etc/subuid` +* [`podman::volume`](#podman--volume): Create a podman volume with defined flags + +### Data types + +* [`Podman::Quadlet_name`](#Podman--Quadlet_name): custom datatype that validates different filenames for quadlet units +* [`Podman::Unit::Container`](#Podman--Unit--Container): custom datatype for container entries of podman container quadlet +* [`Podman::Unit::Pod`](#Podman--Unit--Pod): custom datatype for Volume entries of podman container quadlet +* [`Podman::Unit::Volume`](#Podman--Unit--Volume): custom datatype for Volume entries of podman container quadlet ## Classes @@ -73,31 +81,31 @@ podman::containers: The following parameters are available in the `podman` class: -* [`podman_pkg`](#podman_pkg) -* [`skopeo_pkg`](#skopeo_pkg) -* [`buildah_pkg`](#buildah_pkg) -* [`podman_docker_pkg`](#podman_docker_pkg) -* [`compose_pkg`](#compose_pkg) -* [`machinectl_pkg`](#machinectl_pkg) -* [`buildah_pkg_ensure`](#buildah_pkg_ensure) -* [`podman_docker_pkg_ensure`](#podman_docker_pkg_ensure) -* [`compose_pkg_ensure`](#compose_pkg_ensure) -* [`machinectl_pkg_ensure`](#machinectl_pkg_ensure) -* [`nodocker`](#nodocker) -* [`storage_options`](#storage_options) -* [`rootless_users`](#rootless_users) -* [`enable_api_socket`](#enable_api_socket) -* [`manage_subuid`](#manage_subuid) -* [`file_header`](#file_header) -* [`match_subuid_subgid`](#match_subuid_subgid) -* [`subid`](#subid) -* [`pods`](#pods) -* [`volumes`](#volumes) -* [`images`](#images) -* [`containers`](#containers) -* [`networks`](#networks) - -##### `podman_pkg` +* [`podman_pkg`](#-podman--podman_pkg) +* [`skopeo_pkg`](#-podman--skopeo_pkg) +* [`buildah_pkg`](#-podman--buildah_pkg) +* [`podman_docker_pkg`](#-podman--podman_docker_pkg) +* [`compose_pkg`](#-podman--compose_pkg) +* [`machinectl_pkg`](#-podman--machinectl_pkg) +* [`buildah_pkg_ensure`](#-podman--buildah_pkg_ensure) +* [`podman_docker_pkg_ensure`](#-podman--podman_docker_pkg_ensure) +* [`compose_pkg_ensure`](#-podman--compose_pkg_ensure) +* [`machinectl_pkg_ensure`](#-podman--machinectl_pkg_ensure) +* [`nodocker`](#-podman--nodocker) +* [`storage_options`](#-podman--storage_options) +* [`rootless_users`](#-podman--rootless_users) +* [`enable_api_socket`](#-podman--enable_api_socket) +* [`manage_subuid`](#-podman--manage_subuid) +* [`file_header`](#-podman--file_header) +* [`match_subuid_subgid`](#-podman--match_subuid_subgid) +* [`subid`](#-podman--subid) +* [`pods`](#-podman--pods) +* [`volumes`](#-podman--volumes) +* [`images`](#-podman--images) +* [`containers`](#-podman--containers) +* [`networks`](#-podman--networks) + +##### `podman_pkg` Data type: `String` @@ -105,7 +113,7 @@ The name of the podman package (default 'podman') Default value: `'podman'` -##### `skopeo_pkg` +##### `skopeo_pkg` Data type: `String` @@ -113,7 +121,7 @@ The name of the skopeo package (default 'skopeo') Default value: `'skopeo'` -##### `buildah_pkg` +##### `buildah_pkg` Data type: `String` @@ -121,7 +129,7 @@ The name of the buildah package (default 'buildah') Default value: `'buildah'` -##### `podman_docker_pkg` +##### `podman_docker_pkg` Data type: `String` @@ -129,7 +137,7 @@ The name of the podman-docker package (default 'podman-docker'). Default value: `'podman-docker'` -##### `compose_pkg` +##### `compose_pkg` Data type: `String` @@ -137,7 +145,7 @@ The name of the podman-compose package (default 'podman-compose'). Default value: `'podman-compose'` -##### `machinectl_pkg` +##### `machinectl_pkg` Data type: `String` @@ -145,7 +153,7 @@ The name of the machinectl package (default 'systemd-container'). Default value: `'systemd-container'` -##### `buildah_pkg_ensure` +##### `buildah_pkg_ensure` Data type: `Enum['absent', 'installed']` @@ -153,7 +161,7 @@ The ensure value for the buildah package (default 'absent') Default value: `'absent'` -##### `podman_docker_pkg_ensure` +##### `podman_docker_pkg_ensure` Data type: `Enum['absent', 'installed']` @@ -161,7 +169,7 @@ The ensure value for the podman docker package (default 'installed') Default value: `'installed'` -##### `compose_pkg_ensure` +##### `compose_pkg_ensure` Data type: `Enum['absent', 'installed']` @@ -169,7 +177,7 @@ The ensure value for the podman-compose package (default 'absent') Default value: `'absent'` -##### `machinectl_pkg_ensure` +##### `machinectl_pkg_ensure` Data type: `Enum['absent', 'installed']` @@ -177,7 +185,7 @@ The ensure value for the machinectl package (default 'installed') Default value: `'installed'` -##### `nodocker` +##### `nodocker` Data type: `Enum['absent', 'file']` @@ -186,7 +194,7 @@ Values should be either 'file' or 'absent'. (default is 'absent') Default value: `'absent'` -##### `storage_options` +##### `storage_options` Data type: `Hash` @@ -194,7 +202,7 @@ A hash containing any storage options you wish to set in /etc/containers/storage Default value: `{}` -##### `rootless_users` +##### `rootless_users` Data type: `Array` @@ -202,15 +210,15 @@ An array of users to manage using [`podman::rootless`](#podmanrootless) Default value: `[]` -##### `enable_api_socket` +##### `enable_api_socket` Data type: `Boolean` The enable value of the API socket (default `false`) -Default value: ``false`` +Default value: `false` -##### `manage_subuid` +##### `manage_subuid` Data type: `Boolean` @@ -219,9 +227,9 @@ The implementation uses [concat](https://forge.puppet.com/puppetlabs/concat) fra out the subuid/subgid entries. If you have a large number of entries you may want to manage them with another method. You cannot use the `subuid` and `subgid` defined types unless this is `true`. -Default value: ``false`` +Default value: `false` -##### `file_header` +##### `file_header` Data type: `String` @@ -230,7 +238,7 @@ Default file_header is `# FILE MANAGED BY PUPPET` Default value: `'# FILE MANAGED BY PUPPET'` -##### `match_subuid_subgid` +##### `match_subuid_subgid` Data type: `Boolean` @@ -238,9 +246,9 @@ Enable the `subid` parameter to manage both subuid and subgid entries with the s This setting requires `manage_subuid` to be `true` or it will have no effect. (default is true) -Default value: ``true`` +Default value: `true` -##### `subid` +##### `subid` Data type: `Hash` @@ -250,7 +258,7 @@ Hash key `subuid` is the subordinate UID, and `count` is the number of subordina Default value: `{}` -##### `pods` +##### `pods` Data type: `Hash` @@ -258,7 +266,7 @@ A hash of pods to manage using [`podman::pod`](#podmanpod) Default value: `{}` -##### `volumes` +##### `volumes` Data type: `Hash` @@ -266,7 +274,7 @@ A hash of volumes to manage using [`podman::volume`](#podmanvolume) Default value: `{}` -##### `images` +##### `images` Data type: `Hash` @@ -274,7 +282,7 @@ A hash of images to manage using [`podman::image`](#podmanimage) Default value: `{}` -##### `containers` +##### `containers` Data type: `Hash` @@ -282,7 +290,7 @@ A hash of containers to manage using [`podman::container`](#podmancontainer) Default value: `{}` -##### `networks` +##### `networks` Data type: `Hash` @@ -292,7 +300,7 @@ Default value: `{}` ## Defined types -### `podman::container` +### `podman::container` manage podman container and register as a systemd service @@ -319,36 +327,36 @@ podman::container { 'jenkins': The following parameters are available in the `podman::container` defined type: -* [`image`](#image) -* [`user`](#user) -* [`flags`](#flags) -* [`service_flags`](#service_flags) -* [`command`](#command) -* [`ensure`](#ensure) -* [`enable`](#enable) -* [`update`](#update) -* [`ruby`](#ruby) +* [`image`](#-podman--container--image) +* [`user`](#-podman--container--user) +* [`flags`](#-podman--container--flags) +* [`service_flags`](#-podman--container--service_flags) +* [`command`](#-podman--container--command) +* [`ensure`](#-podman--container--ensure) +* [`enable`](#-podman--container--enable) +* [`update`](#-podman--container--update) +* [`ruby`](#-podman--container--ruby) -##### `image` +##### `image` -Data type: `String` +Data type: `Optional[String]` Container registry source of the image being deployed. Required when `ensure` is `present` but optional when `ensure` is set to `absent`. -Default value: `''` +Default value: `undef` -##### `user` +##### `user` -Data type: `String` +Data type: `Optional[String]` Optional user for running rootless containers. For rootless containers, the user must also be defined as a puppet resource that includes at least 'uid', 'gid', and 'home' attributes. -Default value: `''` +Default value: `undef` -##### `flags` +##### `flags` Data type: `Hash` @@ -366,43 +374,43 @@ YAML representation you can use `~` or `null` as the value. Default value: `{}` -##### `service_flags` +##### `service_flags` Data type: `Hash` When a container is created, a systemd unit file for the container service is generated using the 'podman generate systemd' command. All flags for the -command are supported using the 'service_flags" hash parameter, again using +command are supported using the 'service_flags' hash parameter, again using only the long form of the flag names. Default value: `{}` -##### `command` +##### `command` -Data type: `String` +Data type: `Optional[String]` Optional command to be used as the container entry point. -Default value: `''` +Default value: `undef` -##### `ensure` +##### `ensure` -Data type: `String` +Data type: `Enum['present', 'absent']` Valid values are 'present' or 'absent' Default value: `'present'` -##### `enable` +##### `enable` Data type: `Boolean` Status of the automatically generated systemd service for the container. Valid values are 'running' or 'stopped'. -Default value: ``true`` +Default value: `true` -##### `update` +##### `update` Data type: `Boolean` @@ -412,19 +420,19 @@ value of the running container image with the digest of the registry image. When `false`, the container will only be redeployed when the declared state of the puppet resource is changed. -Default value: ``true`` +Default value: `true` -##### `ruby` +##### `ruby` -Data type: `Stdlib::Unixpath` +Data type: `Optional[Stdlib::Unixpath]` The absolute path to the ruby binary to use in scripts. The default path is '/opt/puppetlabs/puppet/bin/ruby' for Puppetlabs packaged puppet, and '/usr/bin/ruby' for all others. -Default value: `$facts['ruby']['sitedir']` +Default value: `undef` -### `podman::image` +### `podman::image` pull or remove container images @@ -445,28 +453,28 @@ podman::image { 'my_container': The following parameters are available in the `podman::image` defined type: -* [`image`](#image) -* [`ensure`](#ensure) -* [`flags`](#flags) -* [`user`](#user) -* [`exec_env`](#exec_env) +* [`image`](#-podman--image--image) +* [`ensure`](#-podman--image--ensure) +* [`flags`](#-podman--image--flags) +* [`user`](#-podman--image--user) +* [`exec_env`](#-podman--image--exec_env) -##### `image` +##### `image` Data type: `String` The name of the container image to pull, which should be present in a configured container registry. -##### `ensure` +##### `ensure` -Data type: `String` +Data type: `Enum['present', 'absent']` State of the resource must be either `present` or `absent`. Default value: `'present'` -##### `flags` +##### `flags` Data type: `Hash` @@ -475,17 +483,17 @@ long form of the flag name. Default value: `{}` -##### `user` +##### `user` -Data type: `String` +Data type: `Optional[String]` Optional user for running rootless containers. When using this parameter, the user must also be defined as a Puppet resource and must include the 'uid', 'gid', and 'home' -Default value: `''` +Default value: `undef` -##### `exec_env` +##### `exec_env` Data type: `Array` @@ -495,7 +503,7 @@ pulled. Useful for defining a proxy for downloads. For example: Default value: `[]` -### `podman::network` +### `podman::network` Create a podman network with defined flags @@ -514,19 +522,19 @@ podman::network { 'mnetwork': The following parameters are available in the `podman::network` defined type: -* [`ensure`](#ensure) -* [`disable_dns`](#disable_dns) -* [`driver`](#driver) -* [`opts`](#opts) -* [`gateway`](#gateway) -* [`internal`](#internal) -* [`ip_range`](#ip_range) -* [`labels`](#labels) -* [`subnet`](#subnet) -* [`ipv6`](#ipv6) -* [`user`](#user) +* [`ensure`](#-podman--network--ensure) +* [`disable_dns`](#-podman--network--disable_dns) +* [`driver`](#-podman--network--driver) +* [`opts`](#-podman--network--opts) +* [`gateway`](#-podman--network--gateway) +* [`internal`](#-podman--network--internal) +* [`ip_range`](#-podman--network--ip_range) +* [`labels`](#-podman--network--labels) +* [`subnet`](#-podman--network--subnet) +* [`ipv6`](#-podman--network--ipv6) +* [`user`](#-podman--network--user) -##### `ensure` +##### `ensure` Data type: `Enum['present', 'absent']` @@ -534,16 +542,16 @@ State of the resource must be either 'present' or 'absent'. Default value: `'present'` -##### `disable_dns` +##### `disable_dns` Data type: `Boolean` Disables the DNS plugin for this network which if enabled, can perform container to container name resolution. -Default value: ``false`` +Default value: `false` -##### `driver` +##### `driver` Data type: `Enum['bridge', 'macvlan']` @@ -551,7 +559,7 @@ Driver to manage the network. Default value: `'bridge'` -##### `opts` +##### `opts` Data type: `Array[String]` @@ -559,32 +567,32 @@ A list of driver specific options. Default value: `[]` -##### `gateway` +##### `gateway` Data type: `Optional[String]` Define the gateway for the network. Must also provide the subnet. -Default value: ``undef`` +Default value: `undef` -##### `internal` +##### `internal` Data type: `Boolean` Restrict external access of this network. -Default value: ``false`` +Default value: `false` -##### `ip_range` +##### `ip_range` Data type: `Optional[String]` Allocate container IP from a range. The range must be a complete subnet and in CIDR notation. -Default value: ``undef`` +Default value: `undef` -##### `labels` +##### `labels` Data type: `Hash[String,String]` @@ -592,33 +600,33 @@ A hash of metadata labels to set on the network. Default value: `{}` -##### `subnet` +##### `subnet` Data type: `Optional[String]` The subnet in CIDR notation -Default value: ``undef`` +Default value: `undef` -##### `ipv6` +##### `ipv6` Data type: `Boolean` Enable IPv6 (dual-stack) networking. -Default value: ``false`` +Default value: `false` -##### `user` +##### `user` -Data type: `String` +Data type: `Optional[String]` Optional user for creating rootless container networks. For rootless containers, the user must also be defined as a puppet resource that includes at least 'uid', 'gid', and 'home' attributes. -Default value: `''` +Default value: `undef` -### `podman::pod` +### `podman::pod` Create a podman pod with defined flags @@ -638,19 +646,19 @@ podman::pod { 'mypod': The following parameters are available in the `podman::pod` defined type: -* [`ensure`](#ensure) -* [`flags`](#flags) -* [`user`](#user) +* [`ensure`](#-podman--pod--ensure) +* [`flags`](#-podman--pod--flags) +* [`user`](#-podman--pod--user) -##### `ensure` +##### `ensure` -Data type: `String` +Data type: `Enum['present', 'absent']` State of the resource, which must be either 'present' or 'absent'. Default value: `'present'` -##### `flags` +##### `flags` Data type: `Hash` @@ -660,21 +668,157 @@ pod name unless the 'name' flag is included in the hash of flags. Default value: `{}` -##### `user` +##### `user` -Data type: `String` +Data type: `Optional[String]` Optional user for running rootless containers. When using this parameter, the user must also be defined as a Puppet resource and must include the 'uid', 'gid', and 'home' -Default value: `''` +Default value: `undef` + +### `podman::quadlet` + +Generate and manage podman quadlet definitions (podman > 4.4.0) + +* **See also** + * podman-systemd.unit.5 + * https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html + +#### Examples + +##### Run a CentOS Container + +```puppet +quadlet::quadlet{'centos.container': + ensure => present, + unit_entry => { + 'Description' => 'Trivial Container that will be very lazy', + }, + service_entry => { + 'TimeoutStartSec' => '900', + }, + container_entry => { + 'Image' => 'quay.io/centos/centos:latest', + 'Exec' => 'sh -c "sleep inf"' + }, + install_entry => { + 'WantedBy' => 'default.target' + }, + active => true, +} +``` + +#### Parameters + +The following parameters are available in the `podman::quadlet` defined type: + +* [`quadlet`](#-podman--quadlet--quadlet) +* [`ensure`](#-podman--quadlet--ensure) +* [`user`](#-podman--quadlet--user) +* [`mode`](#-podman--quadlet--mode) +* [`active`](#-podman--quadlet--active) +* [`unit_entry`](#-podman--quadlet--unit_entry) +* [`install_entry`](#-podman--quadlet--install_entry) +* [`service_entry`](#-podman--quadlet--service_entry) +* [`container_entry`](#-podman--quadlet--container_entry) +* [`pod_entry`](#-podman--quadlet--pod_entry) +* [`volume_entry`](#-podman--quadlet--volume_entry) + +##### `quadlet` + +Data type: `Podman::Quadlet_name` + +of the quadlet file this is the namevar. + +Default value: `$title` + +##### `ensure` + +Data type: `Enum['present', 'absent']` + +State of the container definition. + +Default value: `'present'` + +##### `user` + +Data type: `String[1]` + +User running the container + +Default value: `'root'` + +##### `mode` + +Data type: `Stdlib::Filemode` + +Filemode of container file. + +Default value: `'0444'` + +##### `active` + +Data type: `Optional[Boolean]` + +Make sure the container is running. + +Default value: `undef` -### `podman::rootless` +##### `unit_entry` + +Data type: `Optional[Systemd::Unit::Unit]` + +The `[Unit]` section definition. + +Default value: `undef` + +##### `install_entry` + +Data type: `Optional[Systemd::Unit::Install]` + +The `[Install]` section definition. + +Default value: `undef` + +##### `service_entry` + +Data type: `Optional[Systemd::Unit::Service]` + +The `[Service]` section definition. + +Default value: `undef` + +##### `container_entry` + +Data type: `Optional[Podman::Unit::Container]` + +The `[Container]` section defintion. + +Default value: `undef` + +##### `pod_entry` + +Data type: `Optional[Podman::Unit::Pod]` + +The `[Pod]` section defintion. + +Default value: `undef` + +##### `volume_entry` + +Data type: `Optional[Podman::Unit::Volume]` + +The `[Volume]` section defintion. + +Default value: `undef` + +### `podman::rootless` Enable a given user to run rootless podman containers as a systemd user service. -### `podman::secret` +### `podman::secret` Manage a podman secret. Create and remove secrets, it cannot replace. @@ -717,13 +861,13 @@ podman::secret{'ora_password': The following parameters are available in the `podman::secret` defined type: -* [`ensure`](#ensure) -* [`path`](#path) -* [`secret`](#secret) -* [`flags`](#flags) -* [`user`](#user) +* [`ensure`](#-podman--secret--ensure) +* [`path`](#-podman--secret--path) +* [`secret`](#-podman--secret--secret) +* [`flags`](#-podman--secret--flags) +* [`user`](#-podman--secret--user) -##### `ensure` +##### `ensure` Data type: `Enum['present','absent']` @@ -731,16 +875,16 @@ State of the resource must be either 'present' or 'absent'. Default value: `'present'` -##### `path` +##### `path` Data type: `Optional[Stdlib::Unixpath]` Load secret from an existing file path The secret and path parameters are mutually exclusive. -Default value: ``undef`` +Default value: `undef` -##### `secret` +##### `secret` Data type: `Optional[Sensitive[String]]` @@ -749,9 +893,9 @@ changed the secret will **NOT** be modified. Best to set a secret version as a label. The secret and path parameters are mutually exclusive. -Default value: ``undef`` +Default value: `undef` -##### `flags` +##### `flags` Data type: `Hash` @@ -762,7 +906,7 @@ If the flags for a secret are modified the secret will be recreated. Default value: `{}` -##### `user` +##### `user` Data type: `Optional[String[1]]` @@ -770,9 +914,9 @@ Optional user for running rootless containers. When using this parameter, the user must also be defined as a Puppet resource and must include the 'uid', 'gid', and 'home' -Default value: ``undef`` +Default value: `undef` -### `podman::subgid` +### `podman::subgid` Define an entry in the `/etc/subgid` file. @@ -791,23 +935,23 @@ podman::subgid { 'myuser': The following parameters are available in the `podman::subgid` defined type: -* [`subgid`](#subgid) -* [`count`](#count) -* [`order`](#order) +* [`subgid`](#-podman--subgid--subgid) +* [`count`](#-podman--subgid--count) +* [`order`](#-podman--subgid--order) -##### `subgid` +##### `subgid` Data type: `Integer` Numerical subordinate group ID -##### `count` +##### `count` Data type: `Integer` Numerical subordinate group ID count -##### `order` +##### `order` Data type: `Integer` @@ -815,7 +959,7 @@ Sequence number for concat fragments# Default value: `10` -### `podman::subuid` +### `podman::subuid` Manage entries in `/etc/subuid` @@ -834,23 +978,23 @@ podman::subuid { 'namevar': The following parameters are available in the `podman::subuid` defined type: -* [`subuid`](#subuid) -* [`count`](#count) -* [`order`](#order) +* [`subuid`](#-podman--subuid--subuid) +* [`count`](#-podman--subuid--count) +* [`order`](#-podman--subuid--order) -##### `subuid` +##### `subuid` Data type: `Integer` Numerical subordinate user ID -##### `count` +##### `count` Data type: `Integer` Numerical subordinate user ID count -##### `order` +##### `order` Data type: `Integer` @@ -858,7 +1002,7 @@ Sequence number for concat fragments Default value: `10` -### `podman::volume` +### `podman::volume` Create a podman volume with defined flags @@ -878,19 +1022,19 @@ podman::volume { 'myvolume': The following parameters are available in the `podman::volume` defined type: -* [`ensure`](#ensure) -* [`flags`](#flags) -* [`user`](#user) +* [`ensure`](#-podman--volume--ensure) +* [`flags`](#-podman--volume--flags) +* [`user`](#-podman--volume--user) -##### `ensure` +##### `ensure` -Data type: `String` +Data type: `Enum['present', 'absent']` State of the resource must be either 'present' or 'absent'. Default value: `'present'` -##### `flags` +##### `flags` Data type: `Hash` @@ -901,13 +1045,140 @@ Volume names are created based on the resoure title (namevar) Default value: `{}` -##### `user` +##### `user` -Data type: `String` +Data type: `Optional[String]` Optional user for running rootless containers. When using this parameter, the user must also be defined as a Puppet resource and must include the 'uid', 'gid', and 'home' -Default value: `''` +Default value: `undef` + +## Data types + +### `Podman::Quadlet_name` + +custom datatype that validates different filenames for quadlet units + +* **See also** + * https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html + +Alias of `Pattern[/^[a-zA-Z0-9:\-_.\\@%]+\.(container|volume|pod|network)$/]` + +### `Podman::Unit::Container` + +custom datatype for container entries of podman container quadlet + +* **See also** + * https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html + +Alias of + +```puppet +Struct[Optional['AddCapability'] => Array[String[1],1], + Optional['Annotation'] => Array[String[1],1], + Optional['AutoUpdate'] => Enum['registry','local'], + Optional['ContainerName'] => String[1], + Optional['DNS'] => Array[Stdlib::IP::Address,0], + Optional['DNSOption'] => Array[String[1],0], + Optional['DNSSearch'] => Array[Stdlib::Fqdn,0], + Optional['DropCapability'] => Array[String[1],0], + Optional['Environment'] => Array[String[1],0], + Optional['EnvironmentFile'] => Array[String[1],0], + Optional['Exec'] => String[1], + Optional['ExposeHostPort'] => Array[Stdlib::Port,0], + Optional['GIDMap'] => Array[String[1],0], + Optional['GlobalArgs'] => Array[String[1],0], + Optional['Group'] => Integer[0], + Optional['HealthCmd'] => String[1], + Optional['HealthOnFailure'] => Enum['none','kill','restart','stop'], + Optional['HealthStartPeriod'] => String[1], + Optional['HealthStartupCmd'] => String[1], + Optional['HealthStartupInterval'] => Variant[Enum['disable'],Integer[0]], + Optional['HealthStartupTimeout'] => String[1], + Optional['HealthTimeout'] => String[1], + Optional['Image'] => String[1], + Optional['IP'] => Stdlib::IP::Address::V4, + Optional['IP6'] => Stdlib::IP::Address::V6, + Optional['Label'] => Variant[String[1],Array[String[1],1]], + Optional['LogDriver'] => Enum['k8s-file','journald','none','passthrough'], + Optional['Mask'] => String[1], + Optional['Mount'] => Array[String[1],0], + Optional['Network'] => String[1], + Optional['NoNewPrivileges'] => Boolean, + Optional['Notify'] => Boolean, + Optional['PidsLimits'] => Integer[-1], + Optional['Pod'] => Pattern[/^[a-zA-Z0-0_-]+\.pod$/], + Optional['PodmanArgs'] => Array[String[1],0], + Optional['PublishPort'] => Array[Variant[Stdlib::Port,String[1]],1], + Optional['Pull'] => Enum['always','missing','never','newer'], + Optional['ReadOnly'] => Boolean, + Optional['ReadOnlyTmpfs'] => Boolean, + Optional['Rootfs'] => String[1], + Optional['RunInit'] => Boolean, + Optional['SeccompProfile'] => String[1], + Optional['Secret'] => Array[String[1],0], + Optional['SecurityLabelDisable'] => Boolean, + Optional['SecurityLabelFileType'] => String[1], + Optional['SecurityLabelNested'] => Boolean, + Optional['ShmSize'] => String[1], + Optional['StopTimeout'] => Integer[1], + Optional['SubGIDMap'] => String[1], + Optional['SubUIDMap'] => String[1], + Optional['Sysctl'] => Array[String[1],0], + Optional['Timezone'] => String[1], + Optional['Tmpfs'] => Array[String[1],0], + Optional['UIDMap'] => Array[String[1],0], + Optional['Ulimit'] => String[1], + Optional['Unmask'] => String[1], + Optional['User'] => String[1], + Optional['UserNS'] => String[1], + Optional['Volume'] => Array[String[1],0], + Optional['WorkingDir'] => Stdlib::Unixpath] +``` + +### `Podman::Unit::Pod` + +custom datatype for Volume entries of podman container quadlet + +* **See also** + * https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html + +Alias of + +```puppet +Struct[Optional['ContainersConfModule'] => Variant[Stdlib::Unixpath,Array[Stdlib::Unixpath,1]], + Optional['GlobalArgs'] => Variant[String[1],Array[String[1],1]], + Optional['Network'] => String[1], + Optional['PodmanArgs'] => Variant[String[1],Array[String[1]]], + Optional['PodName'] => String[1], + Optional['PublishPort'] => Array[Stdlib::Port,1], + Optional['Volume'] => Variant[String[1],Array[String[1],]]] +``` + +### `Podman::Unit::Volume` + +custom datatype for Volume entries of podman container quadlet + +* **See also** + * https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html + +Alias of + +```puppet +Struct[Optional['ContainersConfModule'] => Variant[Stdlib::Unixpath,Array[Stdlib::Unixpath,1]], + Optional['Copy'] => Boolean, + Optional['Device'] => String[1], + Optional['Driver'] => String[1], + Optional['GlobalArgs'] => Variant[String[1],Array[String[1],1]], + Optional['Group'] => String[1], + Optional['Image'] => String[1], + Optional['Label'] => Variant[String[1],Array[String[1],1]], + Optional['Options'] => String[1], + Optional['PodmanArgs'] => Variant[String[1],Array[String[1]]], + Optional['Type'] => String[1], + Optional['User'] => String[1], + Optional['VolumeName'] => String[1]] +``` diff --git a/manifests/quadlet.pp b/manifests/quadlet.pp new file mode 100644 index 0000000..bbc55d0 --- /dev/null +++ b/manifests/quadlet.pp @@ -0,0 +1,150 @@ +# @summary Generate and manage podman quadlet definitions (podman > 4.4.0) +# +# @see podman-systemd.unit.5 https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html +# +# @param quadlet of the quadlet file this is the namevar. +# @param ensure State of the container definition. +# @param user User running the container +# @param mode Filemode of container file. +# @param active Make sure the container is running. +# @param unit_entry The `[Unit]` section definition. +# @param install_entry The `[Install]` section definition. +# @param service_entry The `[Service]` section definition. +# @param container_entry The `[Container]` section defintion. +# @param pod_entry The `[Pod]` section defintion. +# @param volume_entry The `[Volume]` section defintion. +# +# @example Run a CentOS Container +# quadlet::quadlet{'centos.container': +# ensure => present, +# unit_entry => { +# 'Description' => 'Trivial Container that will be very lazy', +# }, +# service_entry => { +# 'TimeoutStartSec' => '900', +# }, +# container_entry => { +# 'Image' => 'quay.io/centos/centos:latest', +# 'Exec' => 'sh -c "sleep inf"' +# }, +# install_entry => { +# 'WantedBy' => 'default.target' +# }, +# active => true, +# } +# +define podman::quadlet ( + Enum['present', 'absent'] $ensure = 'present', + Podman::Quadlet_name $quadlet = $title, + String[1] $user = 'root', + Stdlib::Filemode $mode = '0444', + Optional[Boolean] $active = undef, + Optional[Systemd::Unit::Install] $install_entry = undef, + Optional[Systemd::Unit::Unit] $unit_entry = undef, + Optional[Systemd::Unit::Service] $service_entry = undef, + Optional[Podman::Unit::Container] $container_entry = undef, + Optional[Podman::Unit::Volume] $volume_entry = undef, + Optional[Podman::Unit::Pod] $pod_entry = undef, +) { + $_split = $quadlet.split('[.]') + $_name = $_split[0] + $_type = $_split[1] + # Validate the input and find the service name. + case $_type { + 'container': { + if $volume_entry or $pod_entry { + fail('A volume_entry or pod_entry makes no sense on a container quadlet') + } + $_service = "${_name}.service" + } + 'volume': { + if $container_entry or $pod_entry { + fail('A container_entry or pod_entry makes no sense on a volume quadlet') + } + $_service = "${_name}-volume.service" + } + 'pod': { + if $container_entry or $volume_entry { + fail('A container_entry or volume_entry makes no sense on a pod quadlet') + } + $_service = "${_name}-pod.service" + } + default: { + fail('Should never be here due to typing on quadlet') + } + } + + include podman + + # Determine path + if $user == 'root' { + $_path = '/etc/containers/systemd' + $_group = root + } else { + $_path = "${User[$user]['home']}/.config/containers/systemd" + $_group = User[$user]['gid'] + ensure_resource('podman::rootless', $user, {}) + } + + file { "${_path}/${quadlet}": + ensure => $ensure, + owner => $user, + group => $_group, + mode => $mode, + content => epp('podman/quadlet_file.epp', { + 'unit_entry' => $unit_entry, + 'service_entry' => $service_entry, + 'install_entry' => $install_entry, + 'container_entry' => $container_entry, + 'volume_entry' => $volume_entry, + 'pod_entry' => $pod_entry, + }), + } + + if $user == 'root' { + ensure_resource('systemd::daemon_reload', $quadlet) + File["${_path}/${quadlet}"] ~> Systemd::Daemon_reload[$quadlet] + } else { + File["${_path}/${quadlet}"] ~> Exec["daemon-reload-${user}"] + File["${_path}/${quadlet}"] -> Exec["start_${user}.slice"] + } + + if $active != undef { + if $user == 'root' { + service { $_service: + ensure => $active, + } + + if $ensure == 'absent' { + Service[$_service] -> File["${_path}/${quadlet}"] + File["${_path}/${quadlet}"] ~> Systemd::Daemon_reload[$quadlet] + } else { + File["${_path}/${quadlet}"] ~> Service[$_service] + Systemd::Daemon_reload[$quadlet] ~> Service[$_service] + } + } else { + $_systemctl_user = ['systemd-run','--pipe', '--wait', '--user','--machine',"${user}@.host", 'systemctl','--user'] + if $active == true { + exec { "start-${quadlet}-${user}": + command => $_systemctl_user + ['start', $_service], + unless => [$_systemctl_user + ['is-active', $_service]], + path => $facts['path'], + } + exec { "reload-${quadlet}-${user}": + command => $_systemctl_user + ['try-reload-or-restart', $_service], + refreshonly => true, + path => $facts['path'], + before => Exec["start-${quadlet}-${user}"], + } + File["${_path}/${quadlet}"] ~> Exec["reload-${quadlet}-${user}"] + Exec["daemon-reload-${user}"] ~> Exec["reload-${quadlet}-${user}"] + } else { + exec { "stop-${quadlet}-${user}": + command => $_systemctl_user + ['stop', $_service], + onlyif => [$_systemctl_user + ['is-active', $_service]], + path => $facts['path'], + } + } + } + } +} diff --git a/manifests/rootless.pp b/manifests/rootless.pp index af01d62..ddc6100 100644 --- a/manifests/rootless.pp +++ b/manifests/rootless.pp @@ -15,7 +15,9 @@ ensure_resource('File', [ "${User[$name]['home']}/.config", "${User[$name]['home']}/.config/systemd", - "${User[$name]['home']}/.config/systemd/user" + "${User[$name]['home']}/.config/systemd/user", + "${User[$name]['home']}/.config/containers", + "${User[$name]['home']}/.config/containers/systemd", ], { ensure => directory, owner => $name, @@ -36,6 +38,16 @@ ], } + # Use https://github.com/voxpupuli/puppet-systemd/pull/443 once available + exec { "daemon-reload-${name}": + command => [ + 'systemd-run', '--pipe' , '--wait', '--user', '--machine',"${name}@.host", + '/usr/bin/systemctl', '--user', 'daemon-reload', + ], + refreshonly => true, + path => $facts['path'], + } + if $podman::enable_api_socket { exec { "podman rootless api socket ${name}": command => 'systemctl --user enable --now podman.socket', diff --git a/metadata.json b/metadata.json index fe21a0f..f2d5ac7 100644 --- a/metadata.json +++ b/metadata.json @@ -12,6 +12,10 @@ "name": "puppetlabs/stdlib", "version_requirement": ">= 4.1.0 <= 10.0.0" }, + { + "name": "puppet/systemd", + "version_requirement": ">= 5.0.0 <= 7.0.0" + }, { "name": "puppetlabs/concat", "version_requirement": ">= 2.1.0 <= 10.0.0" diff --git a/spec/defines/quadlet_spec.rb b/spec/defines/quadlet_spec.rb new file mode 100644 index 0000000..0066415 --- /dev/null +++ b/spec/defines/quadlet_spec.rb @@ -0,0 +1,191 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'podman::quadlet' do + on_supported_os.each do |os, os_facts| + context "on #{os}" do + let(:facts) { os_facts } + + context 'with a simple centos container image' do + let(:title) { 'centos.container' } + let(:params) do + { + ensure: 'present', + unit_entry: { + 'Description' => 'Simple centos container', + }, + service_entry: { + 'TimeoutStartSec' => '900', + }, + container_entry: { + 'Image' => 'quay.io/centos/centos:latest', + 'Exec' => 'sh -c "sleep inf"', + 'PublishPort' => [1234, '123.1.1.1:100:102'], + }, + } + end + + it { is_expected.to compile.with_all_deps } + + it { + is_expected.to contain_file('/etc/containers/systemd/centos.container') + .with_ensure('present') + .with_owner('root') + .with_group('root') + .with_mode('0444') + .with_content(%r{^\[Unit\]$}) + .with_content(%r{^Description=Simple centos container$}) + .with_content(%r{^\[Service\]$}) + .with_content(%r{^TimeoutStartSec=900}) + .with_content(%r{^\[Container\]$}) + .with_content(%r{^Image=quay.io/centos/centos:latest$}) + .with_content(%r{^PublishPort=1234$}) + .with_content(%r{^PublishPort=123.1.1.1:100:102$}) + .with_content(%r{^Exec=sh -c "sleep inf"$}) + } + + it { is_expected.to contain_systemd__daemon_reload('centos.container') } + it { is_expected.not_to contain_service('centos.container') } + it { is_expected.not_to contain_service('centos.service') } + + context 'with the container absent' do + let(:params) do + super().merge({ ensure: 'absent' }) + end + + it { is_expected.to contain_file('/etc/containers/systemd/centos.container').with_ensure('absent') } + it { is_expected.to contain_systemd__daemon_reload('centos.container') } + end + + context 'with the service active' do + let(:params) do + super().merge({ active: true }) + end + + it { is_expected.to contain_service('centos.service').with_ensure(true) } + end + + context 'with a user set' do + let(:pre_condition) do + <<~PUPPET + user{'steve': + ensure => present, + uid => 1000, + gid => 1000, + home => '/var/lib/steve', + } + file{'/var/lib/steve': + ensure => directory, + owner => steve, + group => 1000, + mode => '0644', + } + PUPPET + end + let(:params) do + super().merge({ + user: 'steve', + }) + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_podman__rootless('steve') } + context 'with active true' do + let(:params) do + super().merge({ + active: true, + }) + end + + it { is_expected.to contain_exec('start-centos.container-steve') } + it { + is_expected.to contain_exec('start-centos.container-steve').with( + { + command: ['systemd-run', '--pipe', '--wait', '--user', '--machine', 'steve@.host', 'systemctl', '--user', 'start', 'centos.service'], + unless: [['systemd-run', '--pipe', '--wait', '--user', '--machine', 'steve@.host', 'systemctl', '--user', 'is-active', 'centos.service']], + }, + ) + } + end + end + end + context 'with a simple pod quadlet' do + let(:title) { 'mypod.pod' } + let(:params) do + { + ensure: 'present', + unit_entry: { + 'Description' => 'Simple Pod', + }, + service_entry: { + 'TimeoutStartSec' => '900', + }, + pod_entry: { + 'Network' => 'host', + }, + } + end + + it { is_expected.to compile.with_all_deps } + + it { + is_expected.to contain_file('/etc/containers/systemd/mypod.pod') + .with_ensure('present') + .with_owner('root') + .with_group('root') + .with_mode('0444') + .with_content(%r{^\[Unit\]$}) + .with_content(%r{^Description=Simple Pod$}) + .with_content(%r{^\[Pod\]$}) + .with_content(%r{^Network=host$}) + } + context 'with the pod active' do + let(:params) do + super().merge({ active: true }) + end + + it { is_expected.to contain_service('mypod-pod.service') } + end + end + context 'with a simple volume quadlet' do + let(:title) { 'myvolume.volume' } + let(:params) do + { + ensure: 'present', + unit_entry: { + 'Description' => 'Simple Volume', + }, + service_entry: { + 'TimeoutStartSec' => '900', + }, + volume_entry: { + 'Driver' => 'image', + }, + } + end + + it { is_expected.to compile.with_all_deps } + + it { + is_expected.to contain_file('/etc/containers/systemd/myvolume.volume') + .with_ensure('present') + .with_owner('root') + .with_group('root') + .with_mode('0444') + .with_content(%r{^\[Unit\]$}) + .with_content(%r{^Description=Simple Volume$}) + .with_content(%r{^\[Volume\]$}) + .with_content(%r{^Driver=image$}) + } + context 'with the service active' do + let(:params) do + super().merge({ active: true }) + end + + it { is_expected.to contain_service('myvolume-volume.service') } + end + end + end + end +end diff --git a/spec/defines/rootless_spec.rb b/spec/defines/rootless_spec.rb index fe8725c..bf4da62 100644 --- a/spec/defines/rootless_spec.rb +++ b/spec/defines/rootless_spec.rb @@ -79,6 +79,40 @@ ) end + it do + is_expected.to contain_file('/home/testing-title/.config/containers').only_with( + { + 'ensure' => 'directory', + 'owner' => 'testing-title', + 'group' => '1111', + 'mode' => '0700', + 'require' => 'File[/home/testing-title]', + }, + ) + end + + it do + is_expected.to contain_file('/home/testing-title/.config/containers/systemd').only_with( + { + 'ensure' => 'directory', + 'owner' => 'testing-title', + 'group' => '1111', + 'mode' => '0700', + 'require' => 'File[/home/testing-title]', + }, + ) + end + + it do + is_expected.to contain_exec('daemon-reload-testing-title').only_with( + { + 'command' => ['systemd-run', '--pipe', '--wait', '--user', '--machine', 'testing-title@.host', '/usr/bin/systemctl', '--user', 'daemon-reload'], + 'refreshonly' => true, + 'path' => os_facts[:path], + }, + ) + end + it do is_expected.to contain_exec('start_testing-title.slice').only_with( { diff --git a/spec/type_aliases/quadlet_name_spec.rb b/spec/type_aliases/quadlet_name_spec.rb new file mode 100644 index 0000000..c63afec --- /dev/null +++ b/spec/type_aliases/quadlet_name_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Podman::Quadlet_name' do + context 'with a permitted unit name' do + [ + 'centos.container', + 'fast.network', + 'empty.volume', + 'masive.pod', + ].each do |unit| + it { is_expected.to allow_value(unit) } + end + end + + context 'with a illegal unit name' do + [ + 'a space.service', + 'noending', + 'wrong.ending', + 'forward/slash.unit', + ].each do |unit| + it { is_expected.not_to allow_value(unit) } + end + end +end diff --git a/spec/type_aliases/unit_container_spec.rb b/spec/type_aliases/unit_container_spec.rb new file mode 100644 index 0000000..4e2f842 --- /dev/null +++ b/spec/type_aliases/unit_container_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Podman::Unit::Container' do + it { is_expected.to allow_value({ 'Image' => 'busybox' }) } + it { is_expected.to allow_value({ 'PublishPort' => [1234] }) } + it { is_expected.to allow_value({ 'PublishPort' => ['1234-12346'] }) } + it { is_expected.to allow_value({ 'PublishPort' => [1234, '123.111.1.1:55:72'] }) } + it { is_expected.to allow_value({ 'Exec' => '/bin/bash' }) } + it { is_expected.to allow_value({ 'Exec' => './entrypoint.sh' }) } +end diff --git a/spec/type_aliases/unit_pod_spec.rb b/spec/type_aliases/unit_pod_spec.rb new file mode 100644 index 0000000..18e6bd3 --- /dev/null +++ b/spec/type_aliases/unit_pod_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Podman::Unit::Pod' do + it { is_expected.to allow_value({ 'PodName' => 'special' }) } +end diff --git a/spec/type_aliases/unit_volume_spec.rb b/spec/type_aliases/unit_volume_spec.rb new file mode 100644 index 0000000..cbbd0de --- /dev/null +++ b/spec/type_aliases/unit_volume_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Podman::Unit::Volume' do + it { is_expected.to allow_value({ 'Driver' => 'image' }) } +end diff --git a/templates/quadlet_file.epp b/templates/quadlet_file.epp new file mode 100644 index 0000000..785606a --- /dev/null +++ b/templates/quadlet_file.epp @@ -0,0 +1,42 @@ +<%- | + Optional[Hash] $unit_entry, + Optional[Hash] $service_entry, + Optional[Hash] $install_entry, + Optional[Hash] $container_entry, + Optional[Hash] $volume_entry, + Optional[Hash] $pod_entry, +| -%> +<%- + + # List of possible of unit sections, should match the list of _entry variables + # above. + + $_unit_sections = [ + 'Unit', + 'Service', + 'Container', + 'Pod', + 'Volume', + 'Install', +] + +-%> +# Deployed with puppet +# +<%- +$_unit_sections.each | $_section | { + $_values = getvar("${downcase($_section)}_entry") + if $_values { +-%> + +[<%= $_section %>] +<%- + $_values.each | $_key, $_value | { + Array($_value, true).each | $_subvalue | { -%> +<%= $_key %>=<%= $_subvalue %> +<%- + } + } + } +} +-%> diff --git a/types/quadlet_name.pp b/types/quadlet_name.pp new file mode 100644 index 0000000..8f3b996 --- /dev/null +++ b/types/quadlet_name.pp @@ -0,0 +1,3 @@ +# @summary custom datatype that validates different filenames for quadlet units +# @see https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html +type Podman::Quadlet_name = Pattern[/^[a-zA-Z0-9:\-_.\\@%]+\.(container|volume|pod|network)$/] diff --git a/types/unit/container.pp b/types/unit/container.pp new file mode 100644 index 0000000..f384ec6 --- /dev/null +++ b/types/unit/container.pp @@ -0,0 +1,64 @@ +# @summary custom datatype for container entries of podman container quadlet +# @see https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html +type Podman::Unit::Container = Struct[ + Optional['AddCapability'] => Array[String[1],1], + Optional['Annotation'] => Array[String[1],1], + Optional['AutoUpdate'] => Enum['registry','local'], + Optional['ContainerName'] => String[1], + Optional['DNS'] => Array[Stdlib::IP::Address,0], + Optional['DNSOption'] => Array[String[1],0], + Optional['DNSSearch'] => Array[Stdlib::Fqdn,0], + Optional['DropCapability'] => Array[String[1],0], + Optional['Environment'] => Array[String[1],0], + Optional['EnvironmentFile'] => Array[String[1],0], + Optional['Exec'] => String[1], + Optional['ExposeHostPort'] => Array[Stdlib::Port,0], + Optional['GIDMap'] => Array[String[1],0], + Optional['GlobalArgs'] => Array[String[1],0], + Optional['Group'] => Integer[0], + Optional['HealthCmd'] => String[1], + Optional['HealthOnFailure'] => Enum['none','kill','restart','stop'], + Optional['HealthStartPeriod'] => String[1], + Optional['HealthStartupCmd'] => String[1], + Optional['HealthStartupInterval'] => Variant[Enum['disable'],Integer[0]], + Optional['HealthStartupTimeout'] => String[1], + Optional['HealthTimeout'] => String[1], + Optional['Image'] => String[1], + Optional['IP'] => Stdlib::IP::Address::V4, + Optional['IP6'] => Stdlib::IP::Address::V6, + Optional['Label'] => Variant[String[1],Array[String[1],1]], + Optional['LogDriver'] => Enum['k8s-file','journald','none','passthrough'], + Optional['Mask'] => String[1], + Optional['Mount'] => Array[String[1],0], + Optional['Network'] => String[1], + Optional['NoNewPrivileges'] => Boolean, + Optional['Notify'] => Boolean, + Optional['PidsLimits'] => Integer[-1], + Optional['Pod'] => Pattern[/^[a-zA-Z0-0_-]+\.pod$/], + Optional['PodmanArgs'] => Array[String[1],0], + Optional['PublishPort'] => Array[Variant[Stdlib::Port,String[1]],1], + Optional['Pull'] => Enum['always','missing','never','newer'], + Optional['ReadOnly'] => Boolean, + Optional['ReadOnlyTmpfs'] => Boolean, + Optional['Rootfs'] => String[1], + Optional['RunInit'] => Boolean, + Optional['SeccompProfile'] => String[1], + Optional['Secret'] => Array[String[1],0], + Optional['SecurityLabelDisable'] => Boolean, + Optional['SecurityLabelFileType'] => String[1], + Optional['SecurityLabelNested'] => Boolean, + Optional['ShmSize'] => String[1], + Optional['StopTimeout'] => Integer[1], + Optional['SubGIDMap'] => String[1], + Optional['SubUIDMap'] => String[1], + Optional['Sysctl'] => Array[String[1],0], + Optional['Timezone'] => String[1], + Optional['Tmpfs'] => Array[String[1],0], + Optional['UIDMap'] => Array[String[1],0], + Optional['Ulimit'] => String[1], + Optional['Unmask'] => String[1], + Optional['User'] => String[1], + Optional['UserNS'] => String[1], + Optional['Volume'] => Array[String[1],0], + Optional['WorkingDir'] => Stdlib::Unixpath, +] diff --git a/types/unit/pod.pp b/types/unit/pod.pp new file mode 100644 index 0000000..84fdc0d --- /dev/null +++ b/types/unit/pod.pp @@ -0,0 +1,11 @@ +# @summary custom datatype for Volume entries of podman container quadlet +# @see https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html +type Podman::Unit::Pod = Struct[ + Optional['ContainersConfModule'] => Variant[Stdlib::Unixpath,Array[Stdlib::Unixpath,1]], + Optional['GlobalArgs'] => Variant[String[1],Array[String[1],1]], + Optional['Network'] => String[1], + Optional['PodmanArgs'] => Variant[String[1],Array[String[1]]], + Optional['PodName'] => String[1], + Optional['PublishPort'] => Array[Stdlib::Port,1], + Optional['Volume'] => Variant[String[1],Array[String[1],]], +] diff --git a/types/unit/volume.pp b/types/unit/volume.pp new file mode 100644 index 0000000..4b130e6 --- /dev/null +++ b/types/unit/volume.pp @@ -0,0 +1,17 @@ +# @summary custom datatype for Volume entries of podman container quadlet +# @see https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html +type Podman::Unit::Volume = Struct[ + Optional['ContainersConfModule'] => Variant[Stdlib::Unixpath,Array[Stdlib::Unixpath,1]], + Optional['Copy'] => Boolean, + Optional['Device'] => String[1], + Optional['Driver'] => String[1], + Optional['GlobalArgs'] => Variant[String[1],Array[String[1],1]], + Optional['Group'] => String[1], + Optional['Image'] => String[1], + Optional['Label'] => Variant[String[1],Array[String[1],1]], + Optional['Options'] => String[1], + Optional['PodmanArgs'] => Variant[String[1],Array[String[1]]], + Optional['Type'] => String[1], + Optional['User'] => String[1], + Optional['VolumeName'] => String[1], +]