add warn on dnssd name collision change spec
This commit is contained in:
1 parent
30670de93b
commit
3910ab974c
5 files changed
+296
No files matched your search
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-20
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
# Design
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
`entrypoint.sh` starts D-Bus, Avahi and cupsd, configures the managed queue
|
||||||
|
(`lpadmin -D/-o/-E` on every start) and then verifies cupsd answers on TCP 631.
|
||||||
|
Any queue present in cupsd — including ones added manually via the web UI — is
|
||||||
|
advertised as the DNS-SD instance `"<Description> @ <host>"` (queue name when no
|
||||||
|
Description is set). Avahi registers only one instance per name: with two
|
||||||
|
queues deriving the same name, one is silently never advertised, and CUPS 2.4
|
||||||
|
does not rename on collision (upstream `scheduler/dnssd.c`, `TODO:
|
||||||
|
Handle collisions`). Clients that pinned the losing name (macOS stores it in
|
||||||
|
the `dnssd://` device URI) then report the printer offline. See proposal.md for
|
||||||
|
the incident. The container ships `cups-client` (guaranteed `lpstat`),
|
||||||
|
`avahi-utils` and `curl` (Containerfile).
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:**
|
||||||
|
- At startup, detect when two or more queues would derive the same DNS-SD
|
||||||
|
instance name and log an actionable warning naming the queues.
|
||||||
|
- Keep the check strictly best-effort: it must never affect startup success or
|
||||||
|
queue configuration (consistent with the existing warning style under
|
||||||
|
`set -eu`).
|
||||||
|
- Give operators a documented way to confirm and fix the condition by hand
|
||||||
|
(README troubleshooting entry).
|
||||||
|
|
||||||
|
**Non-Goals:**
|
||||||
|
- No automatic remediation (no renaming, no un-sharing, no deleting queues).
|
||||||
|
- No change to how/when `lpadmin` runs — the restart re-registration churn is
|
||||||
|
out of scope for this change.
|
||||||
|
- No client-side tooling; the check runs only inside the container.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
**D1 — Derive names from live cupsd data, not from mDNS.**
|
||||||
|
The check compares *derived* instance names (`Description` or queue name per
|
||||||
|
queue) instead of listening to actual Avahi registrations. Derivation is
|
||||||
|
deterministic and race-free at startup; observing mDNS (`avahi-browse`) would
|
||||||
|
depend on registration timing and only detect collisions after the fact.
|
||||||
|
- *Alternative considered:* post-startup mDNS audit ("shared queue missing from
|
||||||
|
advertisements") — catches more failure modes but is timing-sensitive and
|
||||||
|
needs instance→queue mapping via TXT `rp`; rejected as the primary mechanism,
|
||||||
|
still documented as a manual verification step in the README.
|
||||||
|
|
||||||
|
**D2 — Inputs: `LC_ALL=C lpstat -l -p` for names/Descriptions, `printers.conf`
|
||||||
|
for the shared flag.**
|
||||||
|
- Queue names + Descriptions come from `lpstat -l -p` run with `LC_ALL=C` so the
|
||||||
|
`Description:` line parses regardless of container locale (the live value is
|
||||||
|
authoritative — it is exactly what cupsd will register).
|
||||||
|
- Only shared queues are advertised, so the shared flag comes from
|
||||||
|
`/etc/cups/printers.conf` (`<Printer …>` block: `Shared yes`). When the flag
|
||||||
|
cannot be determined (file not yet flushed after this run's `lpadmin`, queue
|
||||||
|
missing from it), the queue is **treated as shared** — a collision warning
|
||||||
|
for a not-yet-shared queue is still actionable, whereas staying silent on a
|
||||||
|
shared one reproduces the original incident.
|
||||||
|
- *Alternative considered:* `ipptool` Get-Printer-Attributes — richer data, but
|
||||||
|
availability of `ipptool` in the image is not guaranteed by the Containerfile
|
||||||
|
package list; rejected to keep the check dependency-free beyond `cups-client`.
|
||||||
|
|
||||||
|
**D3 — Placement and failure mode.**
|
||||||
|
A `check_dnssd_name_collisions` function runs after the queue configuration
|
||||||
|
blocks and the TCP reachability check, immediately before the final `ready`
|
||||||
|
log line, so the warning is the last thing an operator sees in `podman logs`.
|
||||||
|
It logs through the existing `log()` helper (`[entrypoint] warning: …`
|
||||||
|
followed by indented detail lines, mirroring the TCP-631 error block) and never
|
||||||
|
exits non-zero; every parse failure degrades to "check skipped" (the script
|
||||||
|
runs under `set -eu`, so all command substitutions are guarded with `|| true`).
|
||||||
|
|
||||||
|
**D4 — Collision = exact match on the name base.**
|
||||||
|
The `@ <host>` suffix is identical for every queue on the server, so equality
|
||||||
|
is decided on the base alone; the full derived name (base + `@ <short-host>`)
|
||||||
|
is still printed in the warning so it matches what `dns-sd -B` shows.
|
||||||
|
The remedy text offers the two fixes that worked on the live system: give each
|
||||||
|
queue a unique Description (`lpadmin -D`), or remove the redundant queue
|
||||||
|
(`lpadmin -x`), followed by remove/re-add of the client queue (README details
|
||||||
|
why clients must be re-added).
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- [Queues created *after* startup are not re-checked] → acceptable: the check
|
||||||
|
runs on every container start, and the README documents a manual
|
||||||
|
`dns-sd -B _ipp._tcp` verification for live changes.
|
||||||
|
- [Stale shared flag if `printers.conf` is not flushed in time] → mitigated by
|
||||||
|
assuming shared when unknown (errs toward warning), see D2.
|
||||||
|
- [False warning when a shared flag parse misreads a queue block] → parsing is
|
||||||
|
scoped to `<Printer name>` … `</Printer>` blocks with exact keyword matches;
|
||||||
|
a misread can at worst add a queue to the comparison, and the warning is
|
||||||
|
advisory only.
|
||||||
|
- [Warning becomes noise on intentionally duplicated setups] → duplication is
|
||||||
|
never functional in CUPS/Avahi (one registrant always loses), so there is no
|
||||||
|
legitimate configuration the warning would nag about.
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
|
||||||
|
Log-output-only change: deploy by rebuilding the image and restarting the
|
||||||
|
container (normal quadlet cycle). No state, config or client impact; rollback
|
||||||
|
is reverting `entrypoint.sh`/README.
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
On the live deployment (`podp`), two queues ended up with the identical CUPS
|
||||||
|
Description, so both derived the same DNS-SD instance name
|
||||||
|
(`"<Description> @ <host>"`). Avahi can register only one of them and CUPS 2.4
|
||||||
|
never resolves the collision (upstream `scheduler/dnssd.c` still has
|
||||||
|
`/* TODO: Handle collisions with avahi_alternate_service_name */`), so one queue
|
||||||
|
showed `printer-is-shared=true` but was never advertised. macOS pins the exact
|
||||||
|
instance name into the queue's `dnssd://` device URI; when the pinned name stops
|
||||||
|
being announced, the Mac reports the printer **offline**. Because the collision
|
||||||
|
is deterministic, container restarts never helped and only remove/re-add on the
|
||||||
|
client fixed it — hours of debugging with no server-side signal pointing at the
|
||||||
|
cause.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- **`entrypoint.sh`**: after cupsd is up, derive the DNS-SD instance name of
|
||||||
|
every shared queue (`Description` if set, otherwise the queue name, plus
|
||||||
|
`@ <host>`) and log a prominent, non-fatal warning when two or more queues
|
||||||
|
derive the same instance name — naming the affected queues and the fix
|
||||||
|
(give each queue a unique Description, or delete the redundant queue).
|
||||||
|
- **`README.md`**: new Troubleshooting entry "macOS shows the printer offline
|
||||||
|
after some time" documenting the symptom, root cause (client-side instance
|
||||||
|
name pinning + duplicate-Description collision), verification commands
|
||||||
|
(`dns-sd -B`, `lpstat -l -p`, `ipptool`), and the fix including the
|
||||||
|
remove/re-add step on macOS.
|
||||||
|
- **Docs correction**: the `PRINTER_NAME` description in the `entrypoint.sh`
|
||||||
|
header and in the README currently claims it is the "advertised (DNS-SD)
|
||||||
|
name" — that is only true while no Description is set; the Description
|
||||||
|
overrides it. Correct both places, and clarify the matching requirement in
|
||||||
|
the `printserver/cups` spec (same claim).
|
||||||
|
|
||||||
|
Out of scope: changing how queues are configured (no automatic renaming or
|
||||||
|
de-duplication), and avoiding the `lpadmin` re-registration churn on restart.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
(none)
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
- `printserver/cups`: adds a requirement that the container detects DNS-SD
|
||||||
|
instance-name collisions between shared queues at startup and reports them in
|
||||||
|
the container log, so a queue that silently loses its advertisement is visible
|
||||||
|
to the operator instead of surfacing as an "offline" client hours later.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
- `entrypoint.sh` — one additional startup diagnostic block (log output only;
|
||||||
|
no change to queue creation, advertisement, or exit behaviour).
|
||||||
|
- `README.md` — one new Troubleshooting section; correction of the
|
||||||
|
`PRINTER_NAME` wording.
|
||||||
|
- No changes to `cupsd.conf`, the `Containerfile`, or runtime behaviour seen by
|
||||||
|
clients; the fix for an existing collision is still a manual operator action
|
||||||
|
(documented in the README).
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: DNS-SD name collisions are reported at startup
|
||||||
|
The container SHALL, once CUPS is running, derive the DNS-SD instance name of
|
||||||
|
every shared queue — its Description if set, otherwise its queue name, suffixed
|
||||||
|
with `@ <host>` — and SHALL log a warning when two or more queues derive the
|
||||||
|
same instance name. The warning SHALL name the affected queues and the remedy,
|
||||||
|
and SHALL NOT prevent the container from starting.
|
||||||
|
|
||||||
|
#### Scenario: Two queues derive the same instance name
|
||||||
|
- **WHEN** two shared queues have Descriptions (or queue names) that derive the same DNS-SD instance name
|
||||||
|
- **THEN** startup logs a warning naming the colliding instance name, both affected queues and the remedy (a unique Description per queue, or removing the redundant queue), and the container continues to start normally
|
||||||
|
|
||||||
|
#### Scenario: A queue without a Description collides with another queue's Description
|
||||||
|
- **WHEN** one shared queue has no Description and would advertise its queue name while another shared queue's Description yields the same instance name
|
||||||
|
- **THEN** the collision is reported the same way, because both derive the same advertised name
|
||||||
|
|
||||||
|
#### Scenario: All derived instance names are unique
|
||||||
|
- **WHEN** every shared queue derives a distinct DNS-SD instance name
|
||||||
|
- **THEN** no collision warning is logged and startup proceeds as before
|
||||||
|
|
||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: Printer endpoint and advertised name are configurable at runtime
|
||||||
|
The container SHALL accept the network printer address and the advertised printer name through environment variables so the same image can target a different printer or name without rebuilding.
|
||||||
|
|
||||||
|
#### Scenario: Printer IP is provided
|
||||||
|
- **WHEN** the container is started with `PRINTER_IP` set
|
||||||
|
- **THEN** a device URI is derived from that address and the Brother HL-4150CDN queue is created pointing at it
|
||||||
|
|
||||||
|
#### Scenario: Printer protocol is selected
|
||||||
|
- **WHEN** the container is started with `PRINTER_IP` and `PRINTER_PROTOCOL`
|
||||||
|
- **THEN** the device URI uses that protocol (socket, lpd or ipp), defaulting to socket
|
||||||
|
|
||||||
|
#### Scenario: Full URI override
|
||||||
|
- **WHEN** `PRINTER_URI` is set
|
||||||
|
- **THEN** it is used as the device URI and `PRINTER_IP` is ignored
|
||||||
|
|
||||||
|
#### Scenario: Custom advertised name
|
||||||
|
- **WHEN** the container is started with `PRINTER_NAME`
|
||||||
|
- **THEN** the CUPS queue uses that name, and the advertised DNS-SD instance name (`<name> @ <host>`) is derived from it unless a Description is also set
|
||||||
|
|
||||||
|
#### Scenario: Custom CUPS description
|
||||||
|
- **WHEN** the container is started with `PRINTER_DESCRIPTION`
|
||||||
|
- **THEN** the queue's CUPS Description (Info) is set to that value, and the advertised DNS-SD instance name is derived from the Description instead of the queue name
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. entrypoint: Erkennung von DNS-SD-Namenskollisionen
|
||||||
|
|
||||||
|
- [ ] 1.1 `entrypoint.sh`: Funktion `check_dnssd_name_collisions` ergänzen,
|
||||||
|
aufgerufen nach dem TCP-631-Check und vor
|
||||||
|
`log "ready: CUPS on 631/tcp, mDNS on 5353/udp"`. Queue-Namen und
|
||||||
|
Descriptions via `LC_ALL=C lpstat -l -p` lesen (Locale-fest, live aus
|
||||||
|
cupsd), Shared-Flag je Queue aus `/etc/cups/printers.conf`
|
||||||
|
(`<Printer …>`-Block, `Shared yes`); unbekannter Shared-Status zählt als
|
||||||
|
geteilt. Pro Queue den Anzeigenamen ableiten: Description, sonst
|
||||||
|
Queue-Name.
|
||||||
|
Verifikation: `sh -n entrypoint.sh` läuft fehlerfrei,
|
||||||
|
`grep -n 'check_dnssd_name_collisions' entrypoint.sh` zeigt Definition
|
||||||
|
und Aufruf an der vorgesehenen Stelle.
|
||||||
|
- [ ] 1.2 Bei gleichen abgeleiteten Namen pro Kollisionsgruppe über den
|
||||||
|
bestehenden `log()`-Stil warnen (Mehrzeiler, analog zum TCP-631-Block):
|
||||||
|
kollidierender Anzeigename `<basis> @ <host>`, betroffene Queue-Namen,
|
||||||
|
Abhilfe (einzigartige Description je Queue per `lpadmin -D` oder
|
||||||
|
redundante Queue entfernen per `lpadmin -x`, danach Client-Queue auf dem
|
||||||
|
Mac entfernen und neu hinzufügen — Details im README). Bei eindeutigen
|
||||||
|
Namen keine Warnung ausgeben.
|
||||||
|
Verifikation: `grep -n 'warning:' entrypoint.sh` findet die neuen
|
||||||
|
Warnzeilen inkl. `lpadmin -D`-Remedies; das Verhalten selbst prüfen die
|
||||||
|
Aufgaben 3.2 (Kollision) und 3.3 (keine Kollision).
|
||||||
|
- [ ] 1.3 Alle Parseschritte abfangen, ohne dass `set -eu` den Start abbricht
|
||||||
|
(`|| true` / geblockte Substitutionen): fehlendes oder unlesbares
|
||||||
|
`printers.conf`,
|
||||||
|
fehlerhaftes `lpstat` → Prüfung still überspringen oder als geteilt
|
||||||
|
annehmen, niemals `exit` und niemals Abbruch der Queue-Einrichtung.
|
||||||
|
Verifikation: `sh -n entrypoint.sh` fehlerfrei; Startverhalten mit
|
||||||
|
fehlendem `printers.conf` ist in 3.4 abgedeckt.
|
||||||
|
|
||||||
|
## 2. README: Troubleshooting und PRINTER_NAME-Korrektur
|
||||||
|
|
||||||
|
- [ ] 2.1 `README.md`: neuen Troubleshooting-Abschnitt
|
||||||
|
„macOS shows the printer offline after some time" (englisch, Stil der
|
||||||
|
bestehenden `###`-Abschnitte) einfügen: Symptom (Queue existiert,
|
||||||
|
Status offline, erst nach Stunden/Wechseln sichtbar), Ursache
|
||||||
|
(macOS pinnt den exakten DNS-SD-Instanznamen `"<Description> @ <host>"`
|
||||||
|
in die `dnssd://`-Device-URI; zwei Queues mit gleicher Description
|
||||||
|
kollidieren, Avahi registriert nur eine, CUPS 2.4 benennt nicht um),
|
||||||
|
Diagnose-Befehle (`podman logs` Warnung, `dns-sd -B _ipp._tcp`,
|
||||||
|
`avahi-browse -rt _ipp._tcp`, `lpstat -l -p`, `ipptool -tv`), Heilung
|
||||||
|
(eindeutige Description bzw. `-x`, dann Remove/Re-add auf macOS — mit dem
|
||||||
|
Hinweis, warum Restart allein nicht hilft: Kollision ist deterministisch).
|
||||||
|
Verifikation: jeder im Abschnitt stehende Befehl läuft unverändert gegen
|
||||||
|
den laufenden Container (lokal aus 3.1 bzw. gegen podp).
|
||||||
|
- [ ] 2.2 `PRINTER_NAME`-Wording korrigieren: README-Tabelle („Also used as
|
||||||
|
the name advertised via AirPrint/DNS-SD") und Fließtext unter
|
||||||
|
*Environment variables* sowie die Kopfkommentarzeile in
|
||||||
|
`entrypoint.sh` („advertised (DNS-SD) name") präzisieren: Der
|
||||||
|
Queue-Name ist nur die Basis, solange keine Description gesetzt ist —
|
||||||
|
`PRINTER_DESCRIPTION` (bzw. `-D`) hat Vorrang für den Anzeigenamen.
|
||||||
|
Verifikation: `grep -n 'PRINTER_NAME' README.md entrypoint.sh` zeigt an
|
||||||
|
allen drei Stellen die korrigierte Formulierung mit Description-Vorrang.
|
||||||
|
|
||||||
|
## 3. Integrationstests (podman, lokal)
|
||||||
|
|
||||||
|
- [ ] 3.1 Image bauen und ohne Kollision starten.
|
||||||
|
Verifikation: `podman build -t printserver:latest .` endet ohne Fehler;
|
||||||
|
`podman run …` erreicht „ready: CUPS on 631/tcp, mDNS on 5353/udp";
|
||||||
|
`podman logs` enthält KEINE Kollisions-Warnung (falls 631 nicht bindet:
|
||||||
|
`podman machine ssh -- sudo sysctl -w net.ipv4.ip_unprivileged_port_start=631`
|
||||||
|
wie bei der Vorgängeränderung).
|
||||||
|
- [ ] 3.2 Kollision erzeugen und Warnung nachweisen.
|
||||||
|
Verifikation: zweite Queue mit der Description des ersten Queues anlegen
|
||||||
|
(`podman exec printserver lpadmin -p dup -E -v socket://192.0.2.1:9100
|
||||||
|
-D "<identische Description>"`), danach `podman restart printserver`:
|
||||||
|
`podman logs` enthält die Warnung mit Anzeigename, beiden Queue-Namen und
|
||||||
|
Remedie, und der Start endet trotzdem mit „ready" (Warnung nicht fatal).
|
||||||
|
- [ ] 3.3 Kollision beheben → Warnung verschwindet.
|
||||||
|
Verifikation: `podman exec printserver lpadmin -p dup -D "Dup Queue"`
|
||||||
|
(eindeutige Description), `podman restart printserver`: das Log enthält
|
||||||
|
keine Kollisions-Warnung mehr und endet mit „ready".
|
||||||
|
- [ ] 3.4 Fehlendes `printers.conf` darf den Start nicht brechen.
|
||||||
|
Verifikation: `podman exec printserver sh -c 'cp /etc/cups/printers.conf /tmp/pc.bak && rm /etc/cups/printers.conf'`
|
||||||
|
danach `podman restart printserver`: der Container erreicht „ready";
|
||||||
|
anschließend Status wiederherstellen
|
||||||
|
(`podman exec … cp /tmp/pc.bak /etc/cups/printers.conf`) bzw. den
|
||||||
|
Testcontainer verwerfen.
|
||||||
|
|
||||||
|
## Workflow follow-up
|
||||||
|
|
||||||
|
- README-Abschnitt und Warnung gegen die echte Konstellation auf `podp`
|
||||||
|
prüfen: nach Deploy (Image neu bauen, Quadlet-Restart) muss die Warnung für
|
||||||
|
`laser`/`laserdrucker-im-buero` erscheinen, bis die Descriptions eindeutig
|
||||||
|
gemacht oder die redundante Queue entfernt ist — danach muss sie weg sein.
|
||||||
|
- Nach Review und grünem Durchlauf die Änderung mit `/opsx-archive`
|
||||||
|
archivieren und das Ergebnis prüfen.
|
||||||
Reference in new issue
Block a user