From 186d5039af485c0e1d68245e4895474062ee40b8 Mon Sep 17 00:00:00 2001 From: CroneKorkN Date: Sun, 10 May 2026 11:56:49 +0200 Subject: [PATCH] migrate to bundlewrap 5 - pin bundlewrap ~=5.0 - rewrite non-reading and KeyError-driven metadata reactors per https://docs.bundlewrap.org/guide/migrate_45/ (defaults / metadata.get paths / MetadataUnavailable) - rename custom Download item methods (cdict/sdict/get_auto_deps -> expected_state/actual_state/get_auto_attrs) Co-Authored-By: Claude Opus 4.7 (1M context) --- bundles/apt/metadata.py | 37 ++++++---------------- bundles/download-server/metadata.py | 42 +++++++------------------ bundles/flask/metadata.py | 10 +++--- bundles/influxdb2/metadata.py | 31 +++++++----------- bundles/monitored/metadata.py | 2 +- bundles/network/metadata.py | 6 ++-- bundles/postgresql/metadata.py | 31 +++++++----------- bundles/raspberry-pi/metadata.py | 25 +++++---------- bundles/raspberrymatic-cert/metadata.py | 24 ++++++-------- bundles/telegraf/metadata.py | 22 +++---------- bundles/wireguard/metadata.py | 22 ++++++------- items/download.py | 10 +++--- requirements.txt | 2 +- 13 files changed, 93 insertions(+), 171 deletions(-) diff --git a/bundles/apt/metadata.py b/bundles/apt/metadata.py index b224b2a..ab9b3ec 100644 --- a/bundles/apt/metadata.py +++ b/bundles/apt/metadata.py @@ -5,6 +5,7 @@ defaults = { 'installed': False, }, 'ca-certificates': {}, + 'unattended-upgrades': {}, }, 'config': { 'DPkg': { @@ -22,6 +23,10 @@ defaults = { }, }, 'APT': { + 'Periodic': { + 'Update-Package-Lists': '1', + 'Unattended-Upgrade': '1', + }, 'NeverAutoRemove': { '^firmware-linux.*', '^linux-firmware$', @@ -49,6 +54,11 @@ defaults = { 'Error-Mode': 'any', }, }, + 'Unattended-Upgrade': { + 'Origins-Pattern': { + "origin=*", + }, + }, }, 'sources': {}, }, @@ -107,33 +117,6 @@ def signed_by(metadata): } -@metadata_reactor.provides( - 'apt/config', - 'apt/packages', -) -def unattended_upgrades(metadata): - return { - 'apt': { - 'config': { - 'APT': { - 'Periodic': { - 'Update-Package-Lists': '1', - 'Unattended-Upgrade': '1', - }, - }, - 'Unattended-Upgrade': { - 'Origins-Pattern': { - "origin=*", - }, - }, - }, - 'packages': { - 'unattended-upgrades': {}, - }, - }, - } - - # @metadata_reactor.provides( # 'apt/config', # 'apt/list_changes', diff --git a/bundles/download-server/metadata.py b/bundles/download-server/metadata.py index d4d7bbc..e0988f8 100644 --- a/bundles/download-server/metadata.py +++ b/bundles/download-server/metadata.py @@ -5,6 +5,11 @@ defaults = { 'needs': { 'zfs_dataset:tank/downloads' }, + 'authorized_users': { + f'build-server@{other_node.name}': {} + for other_node in repo.nodes + if other_node.has_bundle('build-server') + }, }, }, 'zfs': { @@ -14,21 +19,13 @@ defaults = { }, }, }, -} - - -@metadata_reactor.provides( - 'systemd-mount' -) -def mount_certs(metadata): - return { - 'systemd-mount': { - '/var/lib/downloads_nginx': { - 'source': '/var/lib/downloads', - 'user': 'www-data', - }, + 'systemd-mount': { + '/var/lib/downloads_nginx': { + 'source': '/var/lib/downloads', + 'user': 'www-data', }, - } + }, +} @metadata_reactor.provides( @@ -47,20 +44,3 @@ def nginx(metadata): }, }, } - - -@metadata_reactor.provides( - 'users/downloads/authorized_users', -) -def ssh_keys(metadata): - return { - 'users': { - 'downloads': { - 'authorized_users': { - f'build-server@{other_node.name}': {} - for other_node in repo.nodes - if other_node.has_bundle('build-server') - }, - }, - }, - } diff --git a/bundles/flask/metadata.py b/bundles/flask/metadata.py index ca333b8..e19e355 100644 --- a/bundles/flask/metadata.py +++ b/bundles/flask/metadata.py @@ -43,11 +43,11 @@ def units(metadata): 'Service': { 'Environment': { f'{k}={v}' - for k, v in conf.get('env', {}).items() + for k, v in metadata.get(f'flask/{name}/env', {}).items() }, - 'User': conf['user'], - 'Group': conf['group'], - 'ExecStart': f"/opt/{name}/venv/bin/gunicorn -w {conf['workers']} -b 127.0.0.1:{conf['port']} --timeout {conf['timeout']} {conf['app_module']}:app" + 'User': metadata.get(f'flask/{name}/user'), + 'Group': metadata.get(f'flask/{name}/group'), + 'ExecStart': f"/opt/{name}/venv/bin/gunicorn -w {metadata.get(f'flask/{name}/workers')} -b 127.0.0.1:{metadata.get(f'flask/{name}/port')} --timeout {metadata.get(f'flask/{name}/timeout')} {metadata.get(f'flask/{name}/app_module')}:app" }, 'Install': { 'WantedBy': { @@ -55,7 +55,7 @@ def units(metadata): } }, } - for name, conf in metadata.get('flask').items() + for name in metadata.get('flask') } } } diff --git a/bundles/influxdb2/metadata.py b/bundles/influxdb2/metadata.py index 4e7aa6c..e05e2e1 100644 --- a/bundles/influxdb2/metadata.py +++ b/bundles/influxdb2/metadata.py @@ -39,6 +39,17 @@ defaults = { }, } +if node.has_bundle('zfs'): + defaults['zfs'] = { + 'datasets': { + 'tank/influxdb': { + 'mountpoint': '/var/lib/influxdb', + 'recordsize': '8192', + 'atime': 'off', + }, + }, + } + @metadata_reactor.provides( 'influxdb/password', 'influxdb/admin_token', @@ -52,26 +63,6 @@ def admin_password(metadata): } -@metadata_reactor.provides( - 'zfs/datasets', -) -def zfs(metadata): - if not node.has_bundle('zfs'): - return {} - - return { - 'zfs': { - 'datasets': { - 'tank/influxdb': { - 'mountpoint': '/var/lib/influxdb', - 'recordsize': '8192', - 'atime': 'off', - }, - }, - }, - } - - @metadata_reactor.provides( 'dns', ) diff --git a/bundles/monitored/metadata.py b/bundles/monitored/metadata.py index 48d9db3..d6bf5c6 100644 --- a/bundles/monitored/metadata.py +++ b/bundles/monitored/metadata.py @@ -50,7 +50,7 @@ def user(metadata): 'sshmon': { conf['vars.command'] for conf in metadata.get('monitoring/services').values() - if conf['check_command'] == 'sshmon' + if conf.get('check_command') == 'sshmon' and conf.get('vars.sudo', None) }, }, diff --git a/bundles/network/metadata.py b/bundles/network/metadata.py index c8492ca..2ced78d 100644 --- a/bundles/network/metadata.py +++ b/bundles/network/metadata.py @@ -37,11 +37,12 @@ def dhcp(metadata): 'modules-load', ) def units(metadata): + networks = metadata.get('network', {}) if node.has_bundle('systemd-networkd'): units = {} modules_load = set() - for network_name, network_conf in metadata.get('network').items(): + for network_name, network_conf in networks.items(): interface_type = network_conf.get('type', None) # network @@ -116,6 +117,7 @@ def units(metadata): 'systemd/units', ) def queuing_disciplines(metadata): + networks = metadata.get('network', {}) if node.has_bundle('systemd-networkd'): return { 'systemd': { @@ -136,7 +138,7 @@ def queuing_disciplines(metadata): 'WantedBy': 'network-online.target', }, } - for network_name, network_conf in metadata.get('network').items() + for network_name, network_conf in networks.items() if 'qdisc' in network_conf }, }, diff --git a/bundles/postgresql/metadata.py b/bundles/postgresql/metadata.py index 2e9950a..9c729a0 100644 --- a/bundles/postgresql/metadata.py +++ b/bundles/postgresql/metadata.py @@ -31,6 +31,17 @@ defaults = { 'grafana_rows': set(), } +if node.has_bundle('zfs'): + defaults['zfs'] = { + 'datasets': { + 'tank/postgresql': { + 'mountpoint': '/var/lib/postgresql', + 'recordsize': '8192', + 'atime': 'off', + }, + }, + } + @metadata_reactor.provides( 'postgresql/conf', @@ -77,26 +88,6 @@ def apt(metadata): -@metadata_reactor.provides( - 'zfs/datasets', -) -def zfs(metadata): - if not node.has_bundle('zfs'): - return {} - - return { - 'zfs': { - 'datasets': { - 'tank/postgresql': { - 'mountpoint': '/var/lib/postgresql', - 'recordsize': '8192', - 'atime': 'off', - }, - }, - }, - } - - @metadata_reactor.provides( 'telegraf/inputs/postgresql/default', ) diff --git a/bundles/raspberry-pi/metadata.py b/bundles/raspberry-pi/metadata.py index cdbef0e..c35a720 100644 --- a/bundles/raspberry-pi/metadata.py +++ b/bundles/raspberry-pi/metadata.py @@ -6,11 +6,19 @@ defaults = { }, } +if node.has_bundle('zfs'): + defaults['zfs'] = { + 'kernel_params': { + 'zfs_txg_timeout': 300, + }, + } + @metadata_reactor.provides( 'telegraf/agent', ) def telegraf(metadata): + metadata.get('telegraf/agent') # only override if telegraf bundle is present return { 'telegraf': { 'agent': { @@ -19,20 +27,3 @@ def telegraf(metadata): }, }, } - - -@metadata_reactor.provides( - 'zfs/kernel_params', - 'zfs/datasets', -) -def zfs(metadata): - if not node.has_bundle('zfs'): - return {} - - return { - 'zfs': { - 'kernel_params': { - 'zfs_txg_timeout': 300, - }, - }, - } diff --git a/bundles/raspberrymatic-cert/metadata.py b/bundles/raspberrymatic-cert/metadata.py index 4852933..1830ab6 100644 --- a/bundles/raspberrymatic-cert/metadata.py +++ b/bundles/raspberrymatic-cert/metadata.py @@ -1,3 +1,13 @@ +defaults = { + 'systemd-timers': { + 'raspberrymatic-cert': { + 'command': '/opt/raspberrymatic-cert', + 'when': 'daily', + }, + }, +} + + @metadata_reactor.provides( 'letsencrypt/domains', ) @@ -11,17 +21,3 @@ def letsencrypt(metadata): }, }, } - - -@metadata_reactor.provides( - 'systemd-timers/raspberrymatic-cert', -) -def systemd_timers(metadata): - return { - 'systemd-timers': { - 'raspberrymatic-cert': { - 'command': '/opt/raspberrymatic-cert', - 'when': 'daily', - } - }, - } diff --git a/bundles/telegraf/metadata.py b/bundles/telegraf/metadata.py index 1248532..9a86ff5 100644 --- a/bundles/telegraf/metadata.py +++ b/bundles/telegraf/metadata.py @@ -7,6 +7,11 @@ defaults = { # needed by crystal plugins: 'libgc-dev': {}, 'libevent-dev': {}, + ( + 'libpcre2-8-0' + if node.os == 'debian' and node.os_version >= (13,) + else 'libpcre3' + ): {}, }, 'sources': { 'influxdata': { @@ -148,20 +153,3 @@ def influxdb(metadata): # crystal based (procio, pressure_stall): -@metadata_reactor.provides( - 'apt/packages/libpcre2-8-0', - 'apt/packages/libpcre3', -) -def libpcre(metadata): - if node.os == 'debian' and node.os_version >= (13,): - libpcre_package = 'libpcre2-8-0' - else: - libpcre_package = 'libpcre3' - - return { - 'apt': { - 'packages': { - libpcre_package: {}, - }, - }, - } diff --git a/bundles/wireguard/metadata.py b/bundles/wireguard/metadata.py index f06e259..efdedaa 100644 --- a/bundles/wireguard/metadata.py +++ b/bundles/wireguard/metadata.py @@ -112,20 +112,18 @@ def systemd_networkd_netdevs(metadata): }, } - for peer, config in { - **metadata.get('wireguard/s2s'), - **metadata.get('wireguard/clients'), - }.items(): - netdev.update({ - f'WireGuardPeer#{peer}': { - 'PublicKey': repo.libs.wireguard.pubkey(config['peer_id']), - 'PresharedKey': repo.libs.wireguard.psk(config['peer_id'], metadata.get('id')), - 'AllowedIPs': ', '.join(config.get('allowed_ips', [])), + for kind in ('s2s', 'clients'): + for peer in metadata.get(f'wireguard/{kind}'): + peer_id = metadata.get(f'wireguard/{kind}/{peer}/peer_id') + netdev[f'WireGuardPeer#{peer}'] = { + 'PublicKey': repo.libs.wireguard.pubkey(peer_id), + 'PresharedKey': repo.libs.wireguard.psk(peer_id, metadata.get('id')), + 'AllowedIPs': ', '.join(metadata.get(f'wireguard/{kind}/{peer}/allowed_ips', [])), 'PersistentKeepalive': 30, } - }) - if config.get('endpoint'): - netdev[f'WireGuardPeer#{peer}']['Endpoint'] = config['endpoint'] + endpoint = metadata.get(f'wireguard/{kind}/{peer}/endpoint', None) + if endpoint: + netdev[f'WireGuardPeer#{peer}']['Endpoint'] = endpoint return { 'systemd': { diff --git a/items/download.py b/items/download.py index 85b836f..892dc2e 100644 --- a/items/download.py +++ b/items/download.py @@ -65,7 +65,8 @@ class Download(Item): url=quote(self.attributes['url']) )) - def cdict(self): + @property + def expected_state(self): """This is how the world should be""" cdict = { 'type': 'download', @@ -85,7 +86,8 @@ class Download(Item): return cdict - def sdict(self): + @property + def actual_state(self): """This is how the world is right now""" path_info = PathInfo(self.node, self.name) if not path_info.exists: @@ -135,10 +137,10 @@ class Download(Item): item=item_id, )) - def get_auto_deps(self, items): + def get_auto_attrs(self, items): deps = [] for item in items: # debian TODO: add other package manager if item.ITEM_TYPE_NAME == 'pkg_apt' and item.name == 'curl': deps.append(item.id) - return deps + return {'needs': deps} diff --git a/requirements.txt b/requirements.txt index 2c847c2..73be135 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -bundlewrap ~=4.0, >=4.24 +bundlewrap ~=5.0, >=5.0.3 pycryptodome PyNaCl PyYAML