Compare commits

...
Author SHA1 Message Date
jschoubben 1bc6daf31b The photo clients publish the endpoint they declare (hq issue 227)
Each declares a web endpoint — 4001, 4012, 4013 — and published a bare 80,
which the mesh has nothing to assign for, so 80 reached the machine and
collided with the reverse proxy. Written the long way, the software's 80 is
published at the port the module declares and the mesh rewrites the outer
half to whatever it assigned.

photos is the one that failed on the control node; the other two are the same
fault waiting for a machine that runs a proxy.
2026-10-04 12:25:28 +02:00
mesh-admin 9208f7409a Merge pull request 'systemd owns its package; systemd-networkd configures networkd and claims none' (#261) from fix/systemd-owns-its-package into main 2026-10-04 10:17:23 +00:00
jochen a80af7a97f systemd owns its package; systemd-networkd configures networkd and claims none
The service manager's package was declared by the networking module, so the
module that is systemd could not own it and had to leave it out. networkd is a
component of systemd: its module configures it. Removing the package resource
from systemd-networkd uninstalls nothing — the host never removes a package
that is not declared absent.
2026-10-04 12:17:08 +02:00
6 changed files with 20 additions and 15 deletions
+1 -1
View File
@@ -45,7 +45,7 @@
"image": "registry-api.novox.be/novox/photos-client@sha256:f87d63ee7bfb44c9f9748b99be6ba6dc0daf955a1d463699e9e7e3009693c0ab", "image": "registry-api.novox.be/novox/photos-client@sha256:f87d63ee7bfb44c9f9748b99be6ba6dc0daf955a1d463699e9e7e3009693c0ab",
"network": "photos-eef", "network": "photos-eef",
"ports": [ "ports": [
"80" "4012:80"
], ],
"names-on-purpose": { "names-on-purpose": {
"registry-api.novox.be": "built outside the mesh, from the application's own repository, and pulled from the registry that built it; moves when that repository is a build source on the git seat (novox/hq ADR 0155, issue 122)" "registry-api.novox.be": "built outside the mesh, from the application's own repository, and pulled from the registry that built it; moves when that repository is a build source on the git seat (novox/hq ADR 0155, issue 122)"
+1 -1
View File
@@ -45,7 +45,7 @@
"image": "registry-api.novox.be/novox/photos-client@sha256:f87d63ee7bfb44c9f9748b99be6ba6dc0daf955a1d463699e9e7e3009693c0ab", "image": "registry-api.novox.be/novox/photos-client@sha256:f87d63ee7bfb44c9f9748b99be6ba6dc0daf955a1d463699e9e7e3009693c0ab",
"network": "photos-filip", "network": "photos-filip",
"ports": [ "ports": [
"80" "4013:80"
], ],
"names-on-purpose": { "names-on-purpose": {
"registry-api.novox.be": "built outside the mesh, from the application's own repository, and pulled from the registry that built it; moves when that repository is a build source on the git seat (novox/hq ADR 0155, issue 122)" "registry-api.novox.be": "built outside the mesh, from the application's own repository, and pulled from the registry that built it; moves when that repository is a build source on the git seat (novox/hq ADR 0155, issue 122)"
+1 -1
View File
@@ -89,7 +89,7 @@
"image": "registry-api.novox.be/novox/photos-admin-client@sha256:f437fa9ed28b29a012f715fb8d9b809a15cff4a672794c620d5d400f57695580", "image": "registry-api.novox.be/novox/photos-admin-client@sha256:f437fa9ed28b29a012f715fb8d9b809a15cff4a672794c620d5d400f57695580",
"network": "photos", "network": "photos",
"ports": [ "ports": [
"80" "4001:80"
], ],
"names-on-purpose": { "names-on-purpose": {
"registry-api.novox.be": "built outside the mesh, from the application's own repository, and pulled from the registry that built it; moves when that repository is a build source on the git seat (novox/hq ADR 0155, issue 122)" "registry-api.novox.be": "built outside the mesh, from the application's own repository, and pulled from the registry that built it; moves when that repository is a build source on the git seat (novox/hq ADR 0155, issue 122)"
+1 -7
View File
@@ -2,7 +2,6 @@
"module": "systemd-networkd", "module": "systemd-networkd",
"version": "1", "version": "1",
"capabilities": [ "capabilities": [
"package-manager",
"service-manager", "service-manager",
"uplink-systemd-networkd" "uplink-systemd-networkd"
], ],
@@ -13,17 +12,12 @@
} }
], ],
"resources": [ "resources": [
{
"id": "package",
"type": "package",
"package": "systemd"
},
{ {
"id": "config", "id": "config",
"type": "file", "type": "file",
"path": "/etc/systemd/network/00-mesh0.network", "path": "/etc/systemd/network/00-mesh0.network",
"mode": "0644", "mode": "0644",
"content": "# Managed by the mesh (module systemd-networkd). Replaced on every push; edit\n# the catalogue instead.\n#\n# This machine's uplink is systemd-networkd's, and the mesh asks one thing of it\n# here (novox/hq ADR 0117): leave the private network's interface alone. mesh0\n# is the mesh's; the mesh brings it up and configures it itself. The mesh never\n# declares a link, an address, a route, a wireless network or its credentials,\n# nor a network file for any of this machine's own interfaces \u2014 those are\n# the operator's, and the link they make is the only channel the mesh reaches\n# this machine over.\n#\n# 00-: networkd applies the first .network file, in alphanumeric order across\n# every directory, that matches an interface, and ignores every later one even\n# if it matches too (systemd.network(5), [Match]). A catch-all of the operator's\n# \u2014 Name=*, Type=ether, a file with no [Match] at all \u2014 sorted before\n# this one would claim mesh0 first. 00 sorts before every numbered prefix the\n# man page recommends.\n#\n# Unmanaged=yes: \"no attempts are made to bring up or configure matching links,\n# equivalent to when there are no matching network files\" (systemd.network(5),\n# [Link], since 233). A match that ends the search, and does nothing else.\n#\n# No DNS setting, because none is needed: networkd never writes\n# /etc/resolv.conf. What it learns from a lease it hands only to\n# systemd-resolved, and the resolver file stays whatever resolv-conf wrote.\n# Whether resolved runs, and what it does with that, is the resolver\n# configuration's question, not the uplink's.\n#\n# The service is reloaded when this file changes, never restarted: a restart\n# drops the links networkd holds, this machine's channel to the mesh among them.\n[Match]\nName=mesh0\n\n[Link]\nUnmanaged=yes\n" "content": "# Managed by the mesh (module systemd-networkd). Replaced on every push; edit\n# the catalogue instead.\n#\n# This machine's uplink is systemd-networkd's, and the mesh asks one thing of it\n# here (novox/hq ADR 0117): leave the private network's interface alone. mesh0\n# is the mesh's; the mesh brings it up and configures it itself. The mesh never\n# declares a link, an address, a route, a wireless network or its credentials,\n# nor a network file for any of this machine's own interfaces — those are\n# the operator's, and the link they make is the only channel the mesh reaches\n# this machine over.\n#\n# 00-: networkd applies the first .network file, in alphanumeric order across\n# every directory, that matches an interface, and ignores every later one even\n# if it matches too (systemd.network(5), [Match]). A catch-all of the operator's\n# — Name=*, Type=ether, a file with no [Match] at all — sorted before\n# this one would claim mesh0 first. 00 sorts before every numbered prefix the\n# man page recommends.\n#\n# Unmanaged=yes: \"no attempts are made to bring up or configure matching links,\n# equivalent to when there are no matching network files\" (systemd.network(5),\n# [Link], since 233). A match that ends the search, and does nothing else.\n#\n# No DNS setting, because none is needed: networkd never writes\n# /etc/resolv.conf. What it learns from a lease it hands only to\n# systemd-resolved, and the resolver file stays whatever resolv-conf wrote.\n# Whether resolved runs, and what it does with that, is the resolver\n# configuration's question, not the uplink's.\n#\n# The service is reloaded when this file changes, never restarted: a restart\n# drops the links networkd holds, this machine's channel to the mesh among them.\n[Match]\nName=mesh0\n\n[Link]\nUnmanaged=yes\n"
}, },
{ {
"id": "service", "id": "service",
+10 -2
View File
@@ -2,7 +2,8 @@
"module": "systemd", "module": "systemd",
"version": "1", "version": "1",
"capabilities": [ "capabilities": [
"service-manager" "service-manager",
"package-manager"
], ],
"claims": [ "claims": [
{ {
@@ -34,5 +35,12 @@
] ]
} }
] ]
} },
"resources": [
{
"id": "package",
"type": "package",
"package": "systemd"
}
]
} }
+6 -3
View File
@@ -156,9 +156,12 @@ test("a unit's name is never an option", async () => {
assert.deepEqual(calls[0].args.slice(-2), ["--", "-x*"]); assert.deepEqual(calls[0].args.slice(-2), ["--", "-x*"]);
}); });
test("the manifest declares no package — the service manager is always there, and networkd declares it too", () => { test("the manifest owns the systemd package — the service manager's own, never a component module's", () => {
const m = JSON.parse(readFileSync(new URL("../module.json", import.meta.url), "utf8")); const m = JSON.parse(readFileSync(new URL("../module.json", import.meta.url), "utf8"));
assert.ok(!(m.resources ?? []).some((r: { type: string }) => r.type === "package")); assert.ok((m.resources ?? []).some((r: { type: string; package?: string }) => r.type === "package" && r.package === "systemd"));
assert.ok(!m.capabilities.includes("package-manager")); assert.ok(m.capabilities.includes("package-manager"));
// networkd is a component of systemd and configures it; it never claims the package.
const networkd = JSON.parse(readFileSync(new URL("../../systemd-networkd/module.json", import.meta.url), "utf8"));
assert.ok(!(networkd.resources ?? []).some((r: { type: string; package?: string }) => r.type === "package" && r.package === "systemd"));
assert.deepEqual(m.claims[0].serves, ["units", "status", "start", "stop", "restart", "enable", "disable", "journal"]); assert.deepEqual(m.claims[0].serves, ["units", "status", "start", "stop", "restart", "enable", "disable", "journal"]);
}); });