summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--facts.d/btrfs.py6
-rw-r--r--manifests/init.pp45
-rw-r--r--manifests/machine.pp89
-rw-r--r--metadata.json29
-rw-r--r--pdk.yaml2
-rw-r--r--templates/systemd/nspawn.epp313
-rw-r--r--types/systemd/bind.pp12
-rw-r--r--types/systemd/nspawn.pp115
-rw-r--r--types/systemd/resourcelimit7
9 files changed, 618 insertions, 0 deletions
diff --git a/facts.d/btrfs.py b/facts.d/btrfs.py
new file mode 100644
index 0000000..dcb6001
--- /dev/null
+++ b/facts.d/btrfs.py
@@ -0,0 +1,6 @@
+#!/usr/bin/env python3
+
+import btrfs
+
+with btrfs.FileSystem('/var/lib/machines') as fs:
+ for subvolume in fs.subvolumes():
diff --git a/manifests/init.pp b/manifests/init.pp
new file mode 100644
index 0000000..3eedbae
--- /dev/null
+++ b/manifests/init.pp
@@ -0,0 +1,45 @@
+# @summary Configures systemd nspawn containers
+# @param config
+# Shared configuration for all machines, as per systemd.nspawn(5).
+#
+# See nspawn::machine's documentation for how it's merged.
+#
+# @param machines
+# Set of machines to be configured. Creates `nspawn::machine` resources.
+# See that resource type for acceptable options.
+#
+# @param template_dir
+# Location of template subvolumes.
+#
+# @param purge
+# Should old .nspawn files be purged.
+class nspawn (
+ Nspawn::Systemdconfig $config,
+ Stdlib::Abspath $template_dir = '/var/lib/templates',
+ Hash[String, Hash[String, Any]] $machines = {},
+ Boolean $purge = true,
+) {
+ # These aren't parameters since they aren't configurable.
+ # However, move them to the parameters if it turns out that
+ # different distributions place these files in different places.
+ # Location of nspawn files.
+ $nspawn_dir = '/etc/systemd/nspawn'
+ # Location of machine subvolumes.
+ $machine_dir = '/var/lib/machines'
+
+ file { $nspawn_dir:
+ ensure => directory,
+ purge => $purge,
+ recurse => true,
+ }
+
+ file { $template_dir:
+ ensure => directory,
+ }
+
+ file { $machine_dir:
+ ensure => directory,
+ }
+
+ create::resources('nspawn::machine', $machines)
+}
diff --git a/manifests/machine.pp b/manifests/machine.pp
new file mode 100644
index 0000000..8b09715
--- /dev/null
+++ b/manifests/machine.pp
@@ -0,0 +1,89 @@
+# @summary Configuration and provisioning for a single container.
+# @param name
+# Will be used for both the directory name, and the hostname in the container.
+# @param template
+# Which template this machine should be configured from.
+#
+# Templates needs to be manually configured behorehand.
+#
+# The value 'none' is special, since it allows the machine to be
+# managed without a template. The template parameter is however
+# required, since a machine without a template needs to be manually
+# configured through some other mean.
+#
+# @param domain
+# Domain part of FQDN of container.
+#
+# @param config
+# Configuration for the machine, as per systemd.nspawn(5).
+# Will be merged with `nspawn::config` per the `$merge` variable.
+#
+# @param merge
+# How this nodes configuration should be merged with the defalut hash.
+#
+# - deep
+# Stdlib's `deep_merge` will be used, with us on the right.
+# - shallow
+# `$nspawn::config + $config`
+# - replace
+# The upstream will be ignored.
+#
+# @param ensure
+define nspawn::machine (
+ Variant[String, Enum['none']] $template,
+ String $domain = $trusted['domain'],
+ Nspawn::Systemdconfig $config,
+ Enum['deep', 'shallow', 'override'] $merge = 'deep',
+ Enum['present', 'absent'] $ensure = 'present',
+) {
+ $root = "${nspawn::machine_dir}/${name}"
+
+ $final_config = $merge ? {
+ 'deep' => deep_merge($nspawn::config, $config),
+ 'shallow' => $nspawn::config + $config,
+ 'override' => $config,
+ }
+
+ file { "${nspawn::nspawn_dir}/${name}.nspawn":
+ ensure => $ensure,
+ content => epp("${module_name}/systemd/nspawn.epp", {
+ 'data' => $final_config
+ }),
+ }
+
+ if $ensure == 'present' {
+ # if $machine_dir has a quota set, then this inherits it
+ unless $template == 'none' {
+ exec { "Initialize ${name} from template":
+ cmd => [
+ 'btrfs', 'snapshot',
+ "${nspawn::template_dir}/${template}",
+ $root,
+ ],
+ creates => $root,
+ path => ['/bin', '/usr/bin', '/sbin', '/usr/sbin'],
+ }
+ }
+
+ file { "${root}/etc/passwd":
+ content => "${name}\n",
+ }
+
+ file_line { "${root}/etc/hosts ::1":
+ line => "::1\t${name}.${domain}\t${name}",
+ match => "${name}.${domain}",
+ path => "${root}/etc/hosts",
+ }
+ } else {
+ service { "systemd-nspawn@${name}":
+ ensure => stopped,
+ enable => false,
+ }
+
+ exec { "Remove btrfs subvolume ${root}":
+ cmd => ['btrfs', 'subvolume', 'delete', $root],
+ onlyif => [['test', '-d', $root]],
+ path => ['/bin', '/usr/bin', '/sbin', '/usr/sbin'],
+ }
+ }
+}
diff --git a/metadata.json b/metadata.json
new file mode 100644
index 0000000..4201259
--- /dev/null
+++ b/metadata.json
@@ -0,0 +1,29 @@
+{
+ "name": "HugoNikanor-nspawn",
+ "version": "0.1.0",
+ "author": "Hugo Hörnquist",
+ "license": "Apache-2.0",
+ "summary": "Manages Systemd-Nspawn containers",
+ "source": "https://git.hornquist.se/puppet/nspawn",
+ "dependencies": [
+ {
+ "name": "puppetlabs/stdlib",
+ "version_requirement": ">= 6.0.0 < 9.0.0"
+ }
+ ],
+ "requirements": [
+ {
+ "name": "puppet",
+ "version_requirement": ">= 5.5.10 < 8.0.0"
+ }
+ ],
+ "operatingsystem_support": [
+ {
+ "operatingsystem": "Archlinux"
+ }
+ ],
+ "pdk-version": "2.5.0",
+ "template-url": "pdk-default#2.5.0",
+ "template-ref": "tags/2.5.0-0-g369d483"
+}
+
diff --git a/pdk.yaml b/pdk.yaml
new file mode 100644
index 0000000..4bef4bd
--- /dev/null
+++ b/pdk.yaml
@@ -0,0 +1,2 @@
+---
+ignore: []
diff --git a/templates/systemd/nspawn.epp b/templates/systemd/nspawn.epp
new file mode 100644
index 0000000..ee79ba0
--- /dev/null
+++ b/templates/systemd/nspawn.epp
@@ -0,0 +1,313 @@
+<%- | Nspawn::Systemd::Nspawn $data | -%>
+
+[Exec]
+<%- $exec = $data['Exec'] -%>
+<%- if $exec['Boot'] == false { -%>
+Boot=no
+<%- } elsif $exec['Boot'] == true { -%>
+Boot=yes
+<%- } -%>
+<%- if $exec['Ephemeral'] == false { -%>
+Ephemeral=no
+<%- } elsif $exec['Ephemeral'] == true { -%>
+Ephemeral=yes
+<%- } -%>
+<%- if $exec['ProcessTwo'] == false { -%>
+ProcessTwo=no
+<%- } elsif $exec['ProcessTwo'] == true { -%>
+ProcessTwo=yes
+<%- } -%>
+<%- unless $exec['Parameters'] =~ Undef { -%>
+<%- if $exec['Parameters'] =~ String { -%>
+Parameters=<%= $exec['Parameters'] %>
+<%- } else { -%>
+Parameters=<%= $exec['Parameters'].map |$param| {
+ if ' ' in $param {
+ if '"' in $param {
+ "'${param}'"
+ } else {
+ "\"${param}\""
+ }
+ } else {
+ $param
+ }
+}.join(' ') %>
+<%- } } -%>
+<%- unless $exec['Environment'] =~ Undef { -%>
+<%- $exec['Environment'].each |$key, $value| { -%>
+Environment=<%= $key %>=<%= $value %>
+<%- } } -%>
+<%- unless $exec['User'] =~ Undef { -%>
+User=<%= $exec['User'] %>
+<%- } -%>
+<%- unless $exec['WorkingDirectory'] =~ Undef { -%>
+WorkingDirectory=<%= $exec['WorkingDirectory'] %>
+<%- } -%>
+<%- unless $exec['PivotRoot'] =~ Undef { -%>
+PivotRoot=<%= $exec['PivotRoot'] %>
+<%- } -%>
+<%- unless $exec['Capability'] =~ Undef { -%>
+Capability=<% if $exec['Capability'] == 'all' {%>all<%} else { %><%= $exec['Capability'].join(' ') -%>
+<%- } } -%>
+<%- unless $exec['DropCapability'] =~ Undef { -%>
+DropCapability=<% if $exec['DropCapability'] == 'all' {%>all<%} else { %><%= $exec['DropCapability'].join(' ') -%>
+<%- } } -%>
+<%- unless $exec['AmbientCapability'] =~ Undef { -%>
+AmbientCapability=<%= $exec['AmbientCapability'].join(' ') -%>
+<%- } -%>
+<%- unless $exec['NoNewPrivileges'] =~ Undef { -%>
+NoNewPrivileges=<%= if $exec['NoNewPrivileges'] { 'yes' } else { 'no' }%>
+<%- } -%>
+<%- unless $exec['KillSignal'] =~ Undef { -%>
+KillSignal=<%= $exec['KillSignal'] %>
+<%- } -%>
+<%- unless $exec['Personality'] =~ Undef { -%>
+Personality=<%= $exec['Personality'] %>
+<%- } -%>
+<%- unless $exec['MachineID'] =~ Undef { -%>
+MachineID=<%= $exec['MachineID'] %>
+<%- } -%>
+<%- unless $exec['PrivateUsers'] =~ Undef { -%>
+PrivateUsers=<%=
+if $exec['PrivateUsers'] =~ Boolean {
+ if $exec['PrivateUsers'] { 'yes' } else { 'no' }
+} elsif $exec['PrivateUsers'] =~ Tuple {
+ "<%= $exec['PrivateUsers'].join(':') %>"
+} else {
+ $exec['PrivateUsers']
+} %><%- } -%>
+<%- unless $exec['NotifyReady'] =~ Undef { -%>
+NotifyReady=<%= if $exec['NotifyReady'] { 'yes' } else { 'no' }%>
+<%- } -%>
+<%- unless $exec['SystemCallFilter'] =~ Undef { -%>
+SystemCallFilter=<%= $exec['SystemCallFilter'].join(' ') %>
+<%- } -%>
+<%- unless $exec['LimitCPU'] =~ Undef { -%>
+<%- if $exec['LimitCPU'] =~ Tuple { -%>
+LimitCPU=<%= $exec['LimitCPU'].join(':') %>
+<%- } else { -%>
+LimitCPU=<%= $exec['LimitCPU'] %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['LimitFSIZE'] =~ Undef { -%>
+<%- if $exec['LimitFSIZE'] =~ Tuple { -%>
+LimitFSIZE=<%= $exec['LimitFSIZE'].join(':') %>
+<%- } else { -%>
+LimitFSIZE=<%= $exec['LimitFSIZE'] %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['LimitDATA'] =~ Undef { -%>
+<%- if $exec['LimitDATA'] =~ Tuple { -%>
+LimitDATA=<%= $exec['LimitDATA'].join(':') %>
+<%- } else { -%>
+LimitDATA=<%= $exec['LimitDATA'] %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['LimitSTACK'] =~ Undef { -%>
+<%- if $exec['LimitSTACK'] =~ Tuple { -%>
+LimitSTACK=<%= $exec['LimitSTACK'].join(':') %>
+<%- } else { -%>
+LimitSTACK=<%= $exec['LimitSTACK'] %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['limitSTACK'] =~ Undef { -%>
+<%- if $exec['limitSTACK'] =~ Tuple { -%>
+limitSTACK=<%= $exec['limitSTACK'].join(':') %>
+<%- } else { -%>
+limitSTACK=<%= $exec['limitSTACK'] %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['LimitCORE'] =~ Undef { -%>
+<%- if $exec['LimitCORE'] =~ Tuple { -%>
+LimitCORE=<%= $exec['LimitCORE'].join(':')%>
+<%- } else { -%>
+LimitCORE=<%= $exec['LimitCORE'] %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['LIMITRSS'] =~ Undef { -%>
+<%- if $exec['LIMITRSS'] =~ Tuple { -%>
+LIMITRSS=<%= $exec['LIMITRSS'].join(':') %>
+<%- } else { -%>
+LIMITRSS=<%= $exec['LIMITRSS'] %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['LimitNOFILE'] =~ Undef { -%>
+<%- if $exec['LimitNOFILE'] =~ Tuple { -%>
+LimitNOFILE=<%= $exec['LimitNOFILE'].join(':') %>
+<%- } else { -%>
+LimitNOFILE=<%= $exec['LimitNOFILE'] %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['LimitAS'] =~ Undef { -%>
+<%- if $exec['LimitAS'] =~ Tuple { -%>
+LimitAS=<%= $exec['LimitAS'].join(':') %>
+<%- } else { -%>
+LimitAS=<%= $exec['LimitAS'] %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['LimitNPROC'] =~ Undef { -%>
+<%- if $exec['LimitNPROC'] =~ Tuple { -%>
+LimitNPROC=<%= $exec['LimitNPROC'].join(':') %>
+<%- } else { -%>
+LimitNPROC=<%= $exec['LimitNPROC'] %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['LimitLOCKS'] =~ Undef { -%>
+<%- if $exec['LimitLOCKS'] =~ Tuple { -%>
+LimitLOCKS=<%= $exec['LimitLOCKS'].join(':') %>
+<%- } else { -%>
+LimitLOCKS=<%= $exec['LimitLOCKS'] %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['LimitSIGPENDING'] =~ Undef { -%>
+<%- if $exec['LimitSIGPENDING'] =~ Tuple { -%>
+LimitSIGPENDING=<%= $exec['LimitSIGPENDING'].join(':') %>
+<%- } else { -%>
+LimitSIGPENDING=<%= $exec['LimitSIGPENDING'] %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['LimitMSGQUEUE'] =~ Undef { -%>
+<%- if $exec['LimitMSGQUEUE'] =~ Tuple { -%>
+LimitMSGQUEUE=<%= $exec['LimitMSGQUEUE'].join(':') %>
+<%- } else { -%>
+LimitMSGQUEUE=<%= $exec['LimitMSGQUEUE'] %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['LimitNICE'] =~ Undef { -%>
+<%- if $exec['LimitNICE'] =~ Tuple { -%>
+LimitNICE=<%= $exec['LimitNICE'].join(':') %>
+<%- } else { -%>
+LimitNICE=<%= $exec['LimitNICE'] %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['LimitRTPRIO'] =~ Undef { -%>
+<%- if $exec['LimitRTPRIO'] =~ Tuple { -%>
+LimitRTPRIO=<%= $exec['LimitRTPRIO'].join(':') %>
+<%- } else { -%>
+LimitRTPRIO=<%= $exec['LimitRTPRIO'] %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['LimitRTTIME'] =~ Undef { -%>
+<%- if $exec['LimitRTTIME'] =~ Tuple { -%>
+LimitRTTIME=<%= $exec['LimitRTTIME'].join(':') %>
+<%- } else { -%>
+LimitRTTIME=<%= $exec['LimitRTTIME'] %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['OOMScoreAdjust'] =~ Undef { -%>
+OOMScoreAdjust=<%= $exec['OOMScoreAdjust'] %>
+<%- } -%>
+<%- unless $exec['CPUAffinity'] =~ Undef { -%>
+CPUAffinity=<%= $exec['CPUAffinity'].map |$aff| {
+ if $aff =~ Tuple {
+ "${aff[0]}-${aff[1]}"
+ } else {
+ "${aff}"
+ }.join(',')
+} %>
+<%- } -%>
+<%- unless $exec['Hostname'] =~ Undef { -%>
+Hostname=<%= $exec['Hostname'] %>
+<%- } -%>
+<%- unless $exec['ResolvConf'] =~ Undef { -%>
+ResolvConf=<%= $exec['ResolvConf'] %>
+<%- } -%>
+<%- unless $exec['Timezone'] =~ Undef { -%>
+Timezone=<%= $exec['Timezone'] %>
+<%- } -%>
+<%- unless $exec['LinkJournal'] =~ Undef { -%>
+LinkJournal=<%= $exec['LinkJournal'] %>
+<%- } -%>
+
+[Files]
+<%- unless $exec['ReadOnly'] =~ Undef { -%>
+ReadOnly=<%= if $exec['ReadOnly'] { 'yes' } else { 'no' } %>
+<%- } -%>
+<%- unless $exec['Volatile'] =~ Undef { -%>
+<%- if $exec['Volatile'] =~ Boolean { -%>
+Volatile=<%= if $exec['Volatile'] { 'yes' } else { 'no' } %>
+<%- } else { -%>
+Volatile=<%= $exec['Volatile'] %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['Bind'] =~ Undef { $exec['Bind'].each |$bind| { -%>
+<%- if $bind =~ Tuple[String, String] { -%>
+Bind=<%= $bind[0] %>:<%= $bind[1] %>
+<%- } elsif $bind =~ Tuple[String, String, Array[String]] { -%>
+Bind=<%= $bind[0] %>:<%= $bind[1] %>:<%= $bind[2].join(':') %>
+<%- } elsif $bind =~ Struct { -%>
+Bind=<%= $bind['source'] %>:<%= $bind['dest'] %><%-
+if $bind['options'] { %>:<%= $bind['options'].join(':') %><%- } -%>
+<%- } else { -%>
+Bind=<%= $exec['Bind'] %>
+<%- } -%>
+<%- }} -%>
+<%- unless $exec['BindReadOnly'] =~ Undef { $exec['Bind'].each |$bind| { -%>
+<%- if $bind =~ Tuple[String, String] { -%>
+BindReadOnly=<%= $bind[0] %>:<%= $bind[1] %>
+<%- } elsif $bind =~ Tuple[String, String, Array[String]] { -%>
+BindReadOnly=<%= $bind[0] %>:<%= $bind[1] %>:<%= $bind[2].join(':') %>
+<%- } elsif $bind =~ Struct { -%>
+BindReadOnly=<%= $bind['source'] %>:<%= $bind['dest'] %><%-
+if $bind['options'] { %>:<%= $bind['options'].join(':') %><%- } -%>
+<%- } else { -%>
+BindReadOnly=<%= $exec['Bind'] %>
+<%- } -%>
+<%- }} -%>
+<%- unless $exec['BindUser'] =~ Undef {$exec['BindUser'].each |$user| { -%>
+BindUser=<%= $user %>
+<%- } } -%>
+<%- unless $exec['TemporaryFileSystem'] =~ Undef {$exec['TemporaryFileSystem'].each |$fs| { -%>
+TemporaryFileSystem=<%=
+if $fs =~ Tuple {
+ $fs.join(':')
+} else {
+ $fs
+}
+%>
+<%- } } -%>
+<%- unless $exec['Inaccessible'] =~ Undef {$exec['Inaccessible'].each |$path| { -%>
+Inaccessible=<%= $path %>
+<%- } } -%>
+<%- unless $exec['Overlay'] =~ Undef {$exec['Overlay'].each |$paths| { -%>
+Overlay=<%= $paths.join(':') %>
+<%- } } -%>
+<%- unless $exec['OverlayReadOnly'] =~ Undef {$exec['OverlayReadOnly'].each |$paths| { -%>
+OverlayReadOnly=<%= $paths.join(':') %>
+<%- } } -%>
+<%- unless $exec['PrivateUsersOwnership'] =~ Undef { -%>
+PrivateUsersOwnership=<%= $exec['PrivateUsersOwnership'] %>
+<%- } -%>
+
+[Network]
+<%- unless $exec['Private'] =~ Undef { -%>
+Private=<%= if $exec['Private'] { 'yes' } else { 'no' } %>
+<%- } -%>
+<%- unless $exec['VirtualEthernet'] =~ Undef { -%>
+VirtualEthernet=<%= if $exec['VirtualEthernet'] { 'yes' } else { 'no' } %>
+<%- } -%>
+<%- unless $exec['VirtualEthernetExtra'] =~ Undef {
+ $exec['VirtualEthernetExtra'].each |$interface| { -%>
+VirtualEthernetExtra=<%= if $interface =~ Tuple { $interface.join(':') } else { $interface } %>
+<%- } -%>
+<%- } -%>
+<%- unless $exec['Interface'] =~ Undef { -%>
+Interface=<%= $exec['Interface'].join(' ') %>
+<%- } -%>
+<%- unless $exec['MACVLAN'] =~ Undef { -%>
+MACVLAN=<%= $exec['MACVLAN'].join(' ') %>
+<%- } -%>
+<%- unless $exec['IPVLAN'] =~ Undef { -%>
+IPVLAN=<%= $exec['IPVLAN'].join(' ') %>
+<%- } -%>
+<%- unless $exec['Bridge'] =~ Undef { -%>
+Bridge=<%= $exec['Bridge'] %>
+<%- } -%>
+<%- unless $exec['Zone'] =~ Undef { -%>
+Zone=<%= $exec['Zone'] %>
+<%- } -%>
+<%- unless $exec['Port'] =~ Undef {
+ $exec['Port'].each |$p| { -%>
+Port=<%= $p.join(':') %>
+<%- } -%>
+<%- } -%>
diff --git a/types/systemd/bind.pp b/types/systemd/bind.pp
new file mode 100644
index 0000000..9554e9a
--- /dev/null
+++ b/types/systemd/bind.pp
@@ -0,0 +1,12 @@
+type Nspawn::Systemd::Bind = Variant[
+ String,
+ Tuple[String, String],
+ # TODO Typecheck options
+ Tuple[String, String, Array[String]],
+ Struct[{
+ 'source' => String,
+ 'dest' => String,
+ # TODO Typecheck options
+ 'options' => Optional[Array[String]],
+ }],
+]
diff --git a/types/systemd/nspawn.pp b/types/systemd/nspawn.pp
new file mode 100644
index 0000000..1b488c8
--- /dev/null
+++ b/types/systemd/nspawn.pp
@@ -0,0 +1,115 @@
+type Nspawn::Systemd::Nspawn = Struct[{
+ 'Exec' => Struct[{
+ 'Boot' => Optional[Boolean],
+ 'Ephemeral' => Optional[Boolean],
+ 'ProcessTwo' => Optional[Boolean],
+ 'Parameters' => Optional[Variant[
+ String,
+ Array[String],
+ ]],
+ 'Environment' => Optional[Hash[String, String]],
+ 'User' => Optional[String],
+ 'WorkingDirectory' => Optional[Stdlib::Unixpath],
+ 'PivotRoot' => Optional[Stdlib::Unixpath],
+ 'Capability' => Optional[Variant[Enum['all'], Array[String]]],
+ 'DropCapability' => Optional[Variant[Enum['all'], Array[String]]],
+ 'AmbientCapability' => Optional[Array[String]],
+ 'NoNewPrivileges' => Optional[Boolean],
+ # See signal(7) for valid signals
+ 'KillSignal' => Optional[String],
+ 'Personality' => Optional[Enum['x86', 'x86-64']],
+ 'MachineID' => Optional[Pattern[/\A[A-fa-f0-9]{32}\Z/]],
+ 'PrivateUsers' => Optional[Variant[
+ Integer,
+ Tuple[Integer, Integer],
+ Boolean,
+ Enum['yes', 'no', 'identity', 'pick']
+ ]],
+ 'NotifyReady' => Optional[Boolean],
+ # If first element is '~', then this is a blacklist
+ 'SystemCallFilter' => Optional[Array[String]],
+ 'LimitCPU' => Optional[Nspawn::Systemd::ResourceLimit],
+ 'LimitFSIZE' => Optional[Nspawn::Systemd::ResourceLimit],
+ 'LimitDATA' => Optional[Nspawn::Systemd::ResourceLimit],
+ 'LimitSTACK' => Optional[Nspawn::Systemd::ResourceLimit],
+ 'LimitCORE' => Optional[Nspawn::Systemd::ResourceLimit],
+ 'LimitRSS' => Optional[Nspawn::Systemd::ResourceLimit],
+ 'LimitNOFILE' => Optional[Nspawn::Systemd::ResourceLimit],
+ 'LimitAS' => Optional[Nspawn::Systemd::ResourceLimit],
+ 'LimitNPROC' => Optional[Nspawn::Systemd::ResourceLimit],
+ 'LimitMEMLOCK' => Optional[Nspawn::Systemd::ResourceLimit],
+ 'LimitLOCKS' => Optional[Nspawn::Systemd::ResourceLimit],
+ 'LimitSIGPENDING' => Optional[Nspawn::Systemd::ResourceLimit],
+ 'LimitMSGQUEUE' => Optional[Nspawn::Systemd::ResourceLimit],
+ 'LimitNICE' => Optional[Nspawn::Systemd::ResourceLimit],
+ 'LimitRTPRIO' => Optional[Nspawn::Systemd::ResourceLimit],
+ 'LimitRTTIME' => Optional[Nspawn::Systemd::ResourceLimit],
+ 'OOMScoreAdjust' => Optional[Integer[-1000, 1000]],
+ 'CPUAffinity' => Optional[Array[Variant[Integer, Tuple[Integer, Integer]]]],
+ 'Hostname' => Optional[String],
+ 'ResolvConf' => Optional[Enum[
+ 'off',
+ 'copy-host',
+ 'copy-static',
+ 'copy-uplink',
+ 'copy-stub',
+ 'replace-host',
+ 'replace-static',
+ 'replace-uplink',
+ 'replace-stub',
+ 'bind-host',
+ 'bind-static',
+ 'bind-uplink',
+ 'bind-stub',
+ 'delete',
+ 'auto',
+ ]],
+ 'Timezone' => Optional[Enum[
+ 'off',
+ 'copy',
+ 'bind',
+ 'symlink',
+ 'delete',
+ 'auto',
+ ]],
+ 'LinkJournal' => Optional[Enum[
+ 'no',
+ 'host',
+ 'try-host',
+ 'guest',
+ 'try-guest',
+ 'auto',
+ ]],
+ }],
+ 'Files' => Struct[{
+ 'ReadOnly' => Optional[Boolean],
+ 'Volatile' => Optional[Variant[Boolean, Enum['state']]],
+ 'Bind' => Optional[Array[Nspawn::Systemd::Bind]],
+ 'BindReadOnly' => Optional[Array[Nspawn::Systemd::Bind]],
+ # TODO Can binduser appear multiple times?
+ 'BindUser' => Optional[Array[String]],
+ # TODO Can tmpfs appear multiple times?
+ # TODO options type
+ 'TemporaryFileSystem' => Optional[Array[Variant[String, Tuple[String, String]]]],
+ 'Inaccessible' => Optional[Array[Stdlib::Unixpath]],
+ 'Overlay' => Optional[Array[Array[String, 2]]],
+ 'OverlayReadOnly' => Optional[Array[Array[String, 2]]],
+ 'PrivateUsersOwnership' => Optional[Enum['off', 'chown', 'map', 'auto']],
+ }],
+ 'Network' => Struct[{
+ 'Private' => Optional[Boolean],
+ 'VirtualEthernet' => Optional[Boolean],
+ 'VirtualEthernetExtra' => Optional[Array[Variant[String, Tuple[String, String]]]],
+ 'Interface' => Optional[Array[String]],
+ 'MACVLAN' => Optional[Array[String]],
+ 'IPVLAN' => Optional[Array[String]],
+ 'Bridge' => Optional[String],
+ 'Zone' => Optional[String],
+ 'Port' => Optional[Array[Variant[
+ Tuple[Enum['tcp', 'udp'], Stdlib::Port, Stdlib::Port],
+ Tuple[Enum['tcp', 'udp'], Stdlib::Port],
+ Tuple[Stdlib::Port, Stdlib::Port],
+ Tuple[Stdlib::Port],
+ ]]],
+ }],
+}]
diff --git a/types/systemd/resourcelimit b/types/systemd/resourcelimit
new file mode 100644
index 0000000..3558fb5
--- /dev/null
+++ b/types/systemd/resourcelimit
@@ -0,0 +1,7 @@
+type Nspawn::Systemd::ResourceLimit = Variant[
+ Variant[Integer, enum['infinity']],
+ Tuple[
+ Variant[Integer, enum['infinity']],
+ Variant[Integer, enum['infinity']],
+ ]
+]