|
| 1 | +# Bug: UDP Tracker Domains Missing from `provision` Output |
| 2 | + |
| 3 | +**Issue**: #412 |
| 4 | +**Parent Epic**: None |
| 5 | +**Related**: #405 - Deploy Hetzner Demo Tracker and Document the Process |
| 6 | + |
| 7 | +## Overview |
| 8 | + |
| 9 | +The `provision` command output includes a `domains` array listing the configured domain |
| 10 | +names for the deployed environment. When UDP trackers have domains configured (via the |
| 11 | +`domain` field of `udp_trackers[].domain` in the environment JSON), those domains are |
| 12 | +absent from the list. Only HTTP-based service domains appear. |
| 13 | + |
| 14 | +Example observed output (Hetzner demo deployment #405): |
| 15 | + |
| 16 | +```json |
| 17 | +{ |
| 18 | + "domains": [ |
| 19 | + "http1.torrust-tracker-demo.com", |
| 20 | + "http2.torrust-tracker-demo.com", |
| 21 | + "api.torrust-tracker-demo.com", |
| 22 | + "grafana.torrust-tracker-demo.com" |
| 23 | + ] |
| 24 | +} |
| 25 | +``` |
| 26 | + |
| 27 | +Expected — UDP domains should also appear: |
| 28 | + |
| 29 | +```json |
| 30 | +{ |
| 31 | + "domains": [ |
| 32 | + "http1.torrust-tracker-demo.com", |
| 33 | + "http2.torrust-tracker-demo.com", |
| 34 | + "udp1.torrust-tracker-demo.com", |
| 35 | + "udp2.torrust-tracker-demo.com", |
| 36 | + "api.torrust-tracker-demo.com", |
| 37 | + "grafana.torrust-tracker-demo.com" |
| 38 | + ] |
| 39 | +} |
| 40 | +``` |
| 41 | + |
| 42 | +The `domains` list is used as a DNS-setup reminder — it tells the operator which |
| 43 | +`A`/`AAAA` records need to point at the server IP. A missing UDP domain means the |
| 44 | +operator does not know they need to create that DNS record. |
| 45 | + |
| 46 | +## Goals |
| 47 | + |
| 48 | +- [ ] Include UDP tracker domain names in the `domains` list of the `provision` output |
| 49 | +- [ ] Ensure the `dns_reminder` view also includes UDP domains |
| 50 | +- [ ] Add or update tests to cover UDP domains appearing in both views |
| 51 | + |
| 52 | +## Specifications |
| 53 | + |
| 54 | +### Root Cause |
| 55 | + |
| 56 | +The `domains` field in `ProvisionDetailsData` is populated in |
| 57 | +`src/presentation/cli/views/commands/provision/view_data/provision_details.rs` by |
| 58 | +calling `services.tls_domain_names()`: |
| 59 | + |
| 60 | +```rust |
| 61 | +let domains = if let Some(ip) = environment.instance_ip() { |
| 62 | + let tracker_config = environment.tracker_config(); |
| 63 | + let grafana_config = environment.grafana_config(); |
| 64 | + let services = ServiceInfo::from_tracker_config(tracker_config, ip, grafana_config); |
| 65 | + services |
| 66 | + .tls_domain_names() // ← only returns TLS service domains |
| 67 | + .iter() |
| 68 | + .map(|s| (*s).to_string()) |
| 69 | + .collect() |
| 70 | +} else { |
| 71 | + vec![] |
| 72 | +}; |
| 73 | +``` |
| 74 | + |
| 75 | +`tls_domain_names()` in |
| 76 | +`src/application/command_handlers/show/info/tracker.rs` returns only the `tls_domains` |
| 77 | +vector — domains associated with HTTPS services (HTTP trackers with TLS proxy, API with |
| 78 | +TLS proxy, Grafana). UDP trackers are not TLS services and are never added to |
| 79 | +`tls_domains`. |
| 80 | + |
| 81 | +`ServiceInfo` already stores UDP tracker URLs in `udp_trackers: Vec<String>` (built by |
| 82 | +`build_udp_tracker_urls`), but these are full announce URLs |
| 83 | +(`udp://udp1.example.com:6969/announce`), not bare domain names. The domain name needs |
| 84 | +to be extracted from those URLs, or a separate accessor for UDP domains needs to be |
| 85 | +added. |
| 86 | + |
| 87 | +The same issue exists in `dns_reminder.rs` which also calls `tls_domain_names()` to |
| 88 | +build the DNS setup hint. |
| 89 | + |
| 90 | +### Solution |
| 91 | + |
| 92 | +Add a method `all_domain_names() -> Vec<&str>` (or rename the existing one) to |
| 93 | +`ServiceInfo` that returns: |
| 94 | + |
| 95 | +1. All TLS service domain names (HTTP trackers, API, Grafana) — currently in `tls_domains` |
| 96 | +2. All UDP tracker domain names — extracted from `UdpTrackerConfig::domain()` (if set) |
| 97 | + |
| 98 | +The `provision_details.rs` and `dns_reminder.rs` callers are then updated to call the |
| 99 | +new method instead of `tls_domain_names()`. |
| 100 | + |
| 101 | +An alternative is to keep `tls_domain_names()` unchanged (it is used for the HTTPS |
| 102 | +hint that reads "configure these domains in /etc/hosts or your DNS before enabling TLS") |
| 103 | +and introduce a separate `all_domain_names()` accessor used only for the DNS reminder |
| 104 | +and provision output `domains` field. This keeps the TLS-specific semantic intact while |
| 105 | +fixing the provision output. |
| 106 | + |
| 107 | +### Affected Modules and Types |
| 108 | + |
| 109 | +#### `src/application/command_handlers/show/info/tracker.rs` |
| 110 | + |
| 111 | +- `ServiceInfo`: add `all_domain_names() -> Vec<&str>` that returns TLS domains plus |
| 112 | + UDP tracker domains that have a `domain` configured. |
| 113 | +- `build_udp_tracker_urls`: no change needed; UDP domains are read directly from |
| 114 | + `UdpTrackerConfig::domain()`. |
| 115 | + |
| 116 | +#### `src/presentation/cli/views/commands/provision/view_data/provision_details.rs` |
| 117 | + |
| 118 | +- `From<&Environment<Provisioned>>` implementation: replace the `tls_domain_names()` |
| 119 | + call with `all_domain_names()`. |
| 120 | + |
| 121 | +#### `src/presentation/cli/views/commands/provision/view_data/dns_reminder.rs` |
| 122 | + |
| 123 | +- Replace the `tls_domain_names()` call with `all_domain_names()`. |
| 124 | + |
| 125 | +### What Does Not Change |
| 126 | + |
| 127 | +- `tls_domain_names()` is kept as-is for the `show` command's HTTPS/TLS hint, which |
| 128 | + should only list TLS domains (the hint is about configuring reverse proxy, not DNS). |
| 129 | +- The UDP tracker URLs in `ServiceInfo.udp_trackers` are unchanged. |
| 130 | +- The domain name in a UDP tracker config is optional; UDP trackers without a configured |
| 131 | + domain produce no entry in `all_domain_names()`. |
| 132 | + |
| 133 | +## Implementation Plan |
| 134 | + |
| 135 | +### Phase 1: Add `all_domain_names()` to `ServiceInfo` |
| 136 | + |
| 137 | +- [ ] In `ServiceInfo` (`tracker.rs`): implement `all_domain_names() -> Vec<&str>` that |
| 138 | + returns the union of TLS domain names and UDP tracker domain names (where |
| 139 | + `udp.domain()` is `Some`) |
| 140 | +- [ ] Keep `tls_domain_names()` unchanged |
| 141 | + |
| 142 | +### Phase 2: Update callers |
| 143 | + |
| 144 | +- [ ] `provision_details.rs`: replace `tls_domain_names()` call with `all_domain_names()` |
| 145 | +- [ ] `dns_reminder.rs`: replace `tls_domain_names()` call with `all_domain_names()` |
| 146 | + |
| 147 | +### Phase 3: Tests |
| 148 | + |
| 149 | +- [ ] `tracker.rs`: add test `it_should_return_all_domain_names_including_udp` that |
| 150 | + asserts UDP tracker domains appear in `all_domain_names()` when a UDP domain is set |
| 151 | +- [ ] `tracker.rs`: add test `it_should_exclude_udp_trackers_without_domain_from_all_domain_names` |
| 152 | + that asserts UDP trackers without a `domain` do not appear |
| 153 | +- [ ] `provision_details.rs` or `text_view.rs`/`json_view.rs`: update or add test |
| 154 | + covering UDP domains in the rendered provision output |
| 155 | +- [ ] Run `cargo test` to verify all tests pass |
| 156 | + |
| 157 | +### Phase 4: Linting and pre-commit |
| 158 | + |
| 159 | +- [ ] Run linters: `cargo run --bin linter all` |
| 160 | +- [ ] Run pre-commit: `./scripts/pre-commit.sh` |
| 161 | + |
| 162 | +## Acceptance Criteria |
| 163 | + |
| 164 | +> **Note for Contributors**: These criteria define what the PR reviewer will check. |
| 165 | +> Use this as your pre-review checklist before submitting the PR to minimize |
| 166 | +> back-and-forth iterations. |
| 167 | +
|
| 168 | +**Quality Checks**: |
| 169 | + |
| 170 | +- [ ] Pre-commit checks pass: `./scripts/pre-commit.sh` |
| 171 | + |
| 172 | +**Task-Specific Criteria**: |
| 173 | + |
| 174 | +- [ ] `provision` output `domains` array includes UDP tracker domain names when the |
| 175 | + environment config supplies a `domain` for UDP trackers |
| 176 | +- [ ] `provision` output `domains` array does **not** include entries for UDP trackers |
| 177 | + that have no `domain` configured (IP-only UDP trackers) |
| 178 | +- [ ] The DNS setup reminder shown after `provision` also includes UDP tracker domains |
| 179 | +- [ ] `tls_domain_names()` is unchanged — the HTTPS-specific TLS hint is not affected |
| 180 | +- [ ] Unit tests for `all_domain_names()` pass for both the UDP-with-domain and |
| 181 | + UDP-without-domain cases |
| 182 | + |
| 183 | +## Related Documentation |
| 184 | + |
| 185 | +- [docs/deployments/hetzner-demo-tracker/commands/provision/bugs.md](../deployments/hetzner-demo-tracker/commands/provision/bugs.md) — original bug report |
| 186 | +- [src/application/command_handlers/show/info/tracker.rs](../../src/application/command_handlers/show/info/tracker.rs) — `ServiceInfo`, `tls_domain_names()` |
| 187 | +- [src/presentation/cli/views/commands/provision/view_data/provision_details.rs](../../src/presentation/cli/views/commands/provision/view_data/provision_details.rs) — `ProvisionDetailsData` and its `From` impl |
| 188 | +- [src/presentation/cli/views/commands/provision/view_data/dns_reminder.rs](../../src/presentation/cli/views/commands/provision/view_data/dns_reminder.rs) — DNS reminder view |
0 commit comments