1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// Copyright 2023 Developers of the reconcile project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Dynamic peer discovery, behind a single port.
//!
//! Every way a [`ReconcileStore`](crate::ReconcileStore) learns about peers goes through the
//! [`Discovery`] port. Two built-in adapters implement it:
//!
//! - [`RandomProbe`] — the **default, speculative** source: one random address per declared network
//! each round ([`Config::with_net`](crate::reconcile_store::Config::with_net)). The probed address
//! might not be a live peer, so it is only used as a one-shot reconciliation target and is **not**
//! added to the known-peer set. This is what auto-discovers peers on a flat or geographically
//! partitioned CIDR. It is [non-authoritative](Discovery::is_authoritative).
//! - [`DnsDiscovery`] — an **authoritative** source for Kubernetes: it resolves a **headless
//! Service** DNS name (one address record per ready pod), so a single lookup yields the real,
//! current peer set, with no API client and no RBAC. Random probing does not fit Kubernetes, where
//! pod IPs are ephemeral and drawn from a large cluster CIDR — a random probe almost never hits a
//! live pod.
//!
//! An **authoritative** result is treated as the current truth: each returned address is seeded into
//! the known-peer set (via [`ReconcileStore::seed_peer`](crate::ReconcileStore::seed_peer)), and a
//! previously-seen member now absent is decommissioned after a grace period (see
//! [`ReconcileStore::with_dns_discovery`](crate::ReconcileStore::with_dns_discovery)). A
//! non-authoritative result is speculative and only steers the current round's targets. In **all**
//! cases discovery feeds the gossip-target set only; it never grants causal-stability *membership*,
//! which a peer must still earn through a genuine authenticated, dated datagram.
use Future;
use IpAddr;
use Pin;
use Arc;
use IpNet;
use RwLock;
use StdRng;
use crateprobe_targets;
/// The future returned by [`Discovery::discover`].
///
/// A boxed future is used (rather than the `async_trait` crate) so the port stays object-safe —
/// it is always consumed behind `Arc<dyn Discovery>` — without pulling in an extra dependency.
pub type DiscoverFuture<'a> =
;
/// A source of candidate peer addresses for the reconciliation engine.
///
/// [`discover`](Self::discover) is called once per discovery round. The result is interpreted
/// according to [`is_authoritative`](Self::is_authoritative):
///
/// - `Ok(addrs)` from an **authoritative** source is a snapshot of the peers that should exist right
/// now: each address refreshes the corresponding known peer, and a previously-seen member that is
/// *absent* is counted toward grace-period decommissioning. From a **non-authoritative** source it
/// is speculative — only the current round's targets, never seeded as known peers.
/// - `Err(_)` is a **transient failure** (e.g. a DNS blip). It MUST NOT be read as "no peers": the
/// store skips the round entirely, so a momentary resolver hiccup never decommissions anyone.
/// The default, **speculative** discovery: one random address per declared network each round.
///
/// This is the historical auto-discovery for flat or geographically partitioned CIDRs.
/// The probed addresses might not be live peers, so they are used only as one-shot reconciliation
/// targets and never seeded as known peers — if a peer exists there, it replies and is registered
/// then. It shares the engine's live `nets` and `rng`, so retuning the topology at runtime
/// immediately changes what is probed.
/// Discovers peers by resolving a DNS name to its set of address records.
///
/// Point this at a Kubernetes **headless** `Service` (`clusterIP: None`): its DNS name resolves to
/// one A/AAAA record per ready pod, so a single lookup yields every peer. Resolution uses
/// [`tokio::net::lookup_host`], i.e. the system resolver (`getaddrinfo`) — no extra dependency, and
/// it honours the in-cluster DNS configuration.