pub struct Consul { /* private fields */ }Expand description
A key in Consul’s KV store, as a configuration source.
Not Clone: the session holds the current token, and two clones logging in
separately would double the login traffic. Wrap it in an Arc if two places
need one.
Implementations§
Source§impl Consul
impl Consul
Sourcepub fn new(address: impl Into<String>, keys: impl Into<Keys>) -> Self
pub fn new(address: impl Into<String>, keys: impl Into<Keys>) -> Self
The key keys, served by the Consul agent at address.
keys is a key — "myapp/db.json" — or a Keys, for the several-keys
and prefix forms.
The format is taken from the key’s extension — myapp/db.json is JSON.
A key without one, and every prefix, needs
with_format.
Sourcepub fn with_format(self, format: Format) -> Self
pub fn with_format(self, format: Format) -> Self
States the format, for a key whose name does not.
Required for Keys::Prefix — a prefix has no extension — and it also
settles a list whose keys name two different formats.
Sourcepub fn with_token(self, token: impl Into<String>) -> Self
pub fn with_token(self, token: impl Into<String>) -> Self
The ACL token to authenticate with.
Shorthand for with_auth(Auth::token(..)). A token that stops working
cannot be replaced, because there are no credentials here to log in
again with; Auth::kubernetes and Auth::jwt can.
Sourcepub fn with_auth(self, auth: Auth) -> Self
pub fn with_auth(self, auth: Auth) -> Self
How to obtain an ACL token.
// In Kubernetes, with no secret to distribute at all.
let consul = Consul::new("http://consul:8500", "myapp/db.json")
.with_auth(Auth::kubernetes("kubernetes"));
// Or whatever the operator put in the environment.
let consul = Consul::new("http://consul:8500", "myapp/db.json")
.with_auth(Auth::from_environment());Logging in is lazy: this reaches nothing, and the first read does it.
Sourcepub fn with_agent(self, agent: Agent) -> Self
pub fn with_agent(self, agent: Agent) -> Self
Uses an HTTP client the program already has.
For a caller with its own proxy settings, a private CA, a client
certificate, or a connection pool it would rather not have a second copy
of. The agent’s own timeout applies instead of
with_timeout — including for the long blocking
query watch issues, so an agent used for watching needs
a timeout above with_wait.
The escape hatch, and it stays one: with_tls covers
a private CA and a client certificate, and everything else — a proxy, a
connection pool, an option this crate has never heard of — still lives
here. Setting both is refused rather than resolved; see
with_tls.
Sourcepub fn with_tls(self, tls: TlsConfig) -> Self
pub fn with_tls(self, tls: TlsConfig) -> Self
A private certificate authority, a client certificate, or both.
The same three settings, spelled the same way, in all seven store
crates — and spelled as data, so nothing here names a ureq type:
let consul = Consul::new("https://consul.internal:8501", "myapp/db.json")
.with_tls(
TlsConfig::new()
.with_ca_certificate_file("/etc/consul.d/consul-agent-ca.pem")
.with_client_certificate_files(
"/etc/consul.d/client.crt",
"/etc/consul.d/client.key",
),
);Consul expresses all of it: a CA from a file or from bytes, and a client
certificate from either. A CA replaces the platform trust store rather
than adding to it — naming a private authority is saying the public ones
do not apply to this host — so a deployment that needs both puts both in
the file. Consul’s own agent CA is exactly this case: consul tls ca create mints an authority no public store has heard of.
There is no way to turn verification off; TlsConfig’s own
documentation argues that one.
Nothing is read here. The files are opened when a request builds its client, so a missing CA is an error naming the path rather than a panic in a builder chain.
§With with_agent
Setting both is refused, at the first request, naming both calls. An agent already carries a complete TLS configuration, so “apply this too” has no meaning that is not a guess — and the guess that loses silently discards a CA, which is the failure this whole surface exists to prevent. Put the CA on the agent, or drop the agent.
Sourcepub fn with_datacenter(self, datacenter: impl Into<String>) -> Self
pub fn with_datacenter(self, datacenter: impl Into<String>) -> Self
The datacenter to read from, when it is not the agent’s own.
Sourcepub fn with_timeout(self, timeout: Duration) -> Self
pub fn with_timeout(self, timeout: Duration) -> Self
How long a single fetch may take before it is given up on. Ten seconds by default.
The deadline for one fetch attempt, excluding retries the
underlying client performs — the same sentence every store in this
family answers to. ureq performs none of its own, so here the
deadline is the whole story.
The blocking query watch issues is the exception, and
deliberately so: it is one fetch that is meant to be held open, so
its client-side timeout is sized from with_wait
plus this value plus the jitter Consul adds.
Sourcepub fn with_wait(self, wait: Duration) -> Self
pub fn with_wait(self, wait: Duration) -> Self
How long a blocking query may hold the connection open, when
watch is used. One minute by default.
This is also how long a stopped watch can take to notice, so it trades one against the other: longer means fewer requests, and a slower exit. Consul’s own ceiling is ten minutes, so anything above it is clamped there — the agent would cap it silently anyway, and this way the client-side timeout stays sized to what the agent will actually do.
Sourcepub fn reporting_to(self, sink: RemoteSink) -> Self
pub fn reporting_to(self, sink: RemoteSink) -> Self
Reports the watch loop’s failed attempts to sink.
A watch loop is the half of a store dynamic-config cannot otherwise
see. RemoteSink::apply records a delivery, so a working watch keeps
RemoteStatus current — but a loop whose blocking query is erroring,
whose key was deleted or whose ACL token was refused delivers nothing,
and without this says nothing: dynamic_config_remote_up would report
the last delivery rather than the last attempt, and an agent that
stopped answering an hour ago would look healthy until something called
refresh_remote.
let sink = DbConfig::remote_sink();
Consul::new(address, "myapp/db.json")
.reporting_to(sink)
.watch(&watching, move |document| sink.apply(document))One sink serves both halves, and it is taken once, where the loop is
wired: a sink is Copy, and the generation it captures there is what
fences a loop winding down after its source was replaced from charging
its failures to the replacement.
A failure to report a failure never reaches the loop — reporting is
infallible and silent — and what it moves is deliberately narrow: the
failure streak and the last failure, never the fetch clock. So
dynamic_config_remote_last_fetch_seconds keeps ageing while
dynamic_config_remote_up goes to zero, which is the pair that says
both the store is not answering and how stale what it last said has
become.
A fetch needs none of this: a fetch records
itself, through the Remote that performed it.
Sourcepub fn watch<F>(&self, watching: &Watching, on_change: F) -> Result<(), Error>
pub fn watch<F>(&self, watching: &Watching, on_change: F) -> Result<(), Error>
Calls on_change whenever what this source reads changes.
Uses Consul’s blocking queries: each request carries the index the last
one returned, and the agent holds it open until that index moves or
with_wait expires. So this is change-driven, not a
poll — the callback runs when the value actually moves.
One key or a prefix. A prefix watch is the cheapest correct watch on
a set anywhere in this family, because it needs no re-read at all: a
recursive blocking query’s answer is the subtree at one index, so the
document handed to on_change is folded from the very bytes the agent
blocked to send. There is no window between “the set changed” and
“read the set” for a second write to land in. A named list is
refused; the reason is on Keys::Several.
The current value is not delivered at startup, for the same reason a file watcher does not report an edit when it starts. Fetch first if the starting value matters, which it usually does:
sink.apply(consul.fetch()?)?;
consul.watch(&watching, move |document| sink.apply(document))A failed query does not end the watch: the agent restarting, a network
blip, or a key that does not exist yet are all exactly what a watch is
supposed to survive. It pauses briefly and tries again, and gives up only
when watching says to. A document identical to the last one is not
reported — Consul bumps the index on every write, including one that
changed nothing.
Surviving a failure quietly is not the same as hiding it:
reporting_to hands each failed attempt to a
RemoteSink, so a loop that has been erroring for an hour stops
reporting the store as healthy.
§Errors
If on_change returns an error, which ends the watch — so a caller that
wants to survive a bad document should log it and return Ok. Transport
failures do not surface here; they are retried.
Under a prefix, also if the subtree cannot be folded into a document: two keys supplying the same path, a key the agent answered with that is not under the prefix, or more keys than the budget. None of those is a blip a retry cures, and retrying them forever with nothing said would leave the configuration frozen and silent — the failure this crate’s watch loops are shaped to avoid.
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for Consul
impl !RefUnwindSafe for Consul
impl !UnwindSafe for Consul
impl Send for Consul
impl Sync for Consul
impl Unpin for Consul
impl UnsafeUnpin for Consul
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
Source§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the foreground set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red() and
green(), which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg():
use yansi::{Paint, Color};
painted.fg(Color::White);Set foreground color to white using white().
use yansi::Paint;
painted.white();Source§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
Source§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
Source§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
Source§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
Source§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
Source§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
Source§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
Source§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
Source§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the background set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red() and
on_green(), which have the same functionality but
are pithier.
§Example
Set background color to red using fg():
use yansi::{Paint, Color};
painted.bg(Color::Red);Set background color to red using on_red().
use yansi::Paint;
painted.on_red();Source§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
Source§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
Source§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
Source§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
Source§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
Source§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
Source§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
Source§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
Source§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
Source§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
Source§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling Attribute value.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold() and
underline(), which have the same functionality
but are pithier.
§Example
Make text bold using attr():
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);Make text bold using using bold().
use yansi::Paint;
painted.bold();Source§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
Source§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi Quirk value.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask() and
wrap(), which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk():
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);Enable wrapping using wrap().
use yansi::Paint;
painted.wrap();Source§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
fn clear(&self) -> Painted<&T>
renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
Source§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the Condition value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted only when both stdout and stderr are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);