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