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) <noreply@anthropic.com>
This commit is contained in:
CroneKorkN 2026-05-10 11:56:49 +02:00
parent e99fd4b1a4
commit 186d5039af
Signed by: cronekorkn
SSH key fingerprint: SHA256:v0410ZKfuO1QHdgKBsdQNF64xmTxOF8osF1LIqwTcVw
13 changed files with 93 additions and 171 deletions

View file

@ -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',

View file

@ -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')
},
},
},
}

View file

@ -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')
}
}
}

View file

@ -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',
)

View file

@ -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)
},
},

View file

@ -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
},
},

View file

@ -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',
)

View file

@ -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,
},
},
}

View file

@ -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',
}
},
}

View file

@ -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: {},
},
},
}

View file

@ -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': {

View file

@ -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}

View file

@ -1,4 +1,4 @@
bundlewrap ~=4.0, >=4.24
bundlewrap ~=5.0, >=5.0.3
pycryptodome
PyNaCl
PyYAML