pub struct Vault { /* private fields */ }Expand description
A secret in Vault’s KV v2 store, as a configuration source.
Not Clone: the session holds the current token, and two clones sharing a
path while logging in separately would double the login traffic and halve
the usefulness of the cache. Wrap it in an Arc if two places need one.
Implementations§
Source§impl Vault
impl Vault
Sourcepub fn new(
address: impl Into<String>,
mount: impl Into<String>,
path: impl Into<Keys>,
) -> Self
pub fn new( address: impl Into<String>, mount: impl Into<String>, path: impl Into<Keys>, ) -> Self
A secret at {mount}/{path}, served by the Vault at address.
path is a path — "myapp/db" — or a Keys, for the several-paths
form.
The document is wrapped under the section key the configuration type
uses — "db" by default, changed with with_key —
because Vault stores a section’s contents, not a whole file. Several
paths all land under that one key and merge, later winning.
Sourcepub fn with_key(self, key: impl Into<String>) -> Self
pub fn with_key(self, key: impl Into<String>) -> Self
The section key to wrap the secret under.
Must match the key the config type’s builder(..) was given.
Sourcepub fn with_token(self, token: impl Into<String>) -> Self
pub fn with_token(self, token: impl Into<String>) -> Self
A token somebody already obtained.
Shorthand for with_auth(Auth::token(..)). A renewable token is still
renewed; a token that stops working cannot be replaced, because there
are no credentials here to log in again with. Every other Auth can.
Sourcepub fn with_auth(self, auth: Auth) -> Self
pub fn with_auth(self, auth: Auth) -> Self
How to obtain a token.
// In Kubernetes, with no secret to distribute at all.
let vault = Vault::new("https://vault.internal:8200", "secret", "myapp/db")
.with_auth(Auth::kubernetes("myapp"));
// Or AppRole, for a service outside it.
let vault = Vault::new("https://vault.internal:8200", "secret", "myapp/db")
.with_auth(Auth::app_role(
std::env::var("VAULT_ROLE_ID").unwrap(),
std::env::var("VAULT_SECRET_ID").unwrap(),
));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.
let vault = Vault::new("https://vault.internal:8200", "secret", "myapp/db")
.with_agent(agent);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 vault = Vault::new("https://vault.internal:8200", "secret", "myapp/db")
.with_token(std::env::var("VAULT_TOKEN").unwrap())
.with_tls(
TlsConfig::new()
.with_ca_certificate_file("/etc/ssl/private-ca.pem")
.with_client_certificate_files("/etc/ssl/app.crt", "/etc/ssl/app.key"),
);Vault 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.
There is no way to turn verification off. The reasoning is in
TlsConfig’s own documentation, and the short version is that the
answer to a self-signed server is to trust its certificate, not to stop
checking.
Nothing is read here. The files are opened when the first request builds the client, so a missing CA is an error naming the path rather than a panic in a builder chain — the same laziness every other constructor in this family has.
§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_namespace(self, namespace: impl Into<String>) -> Self
pub fn with_namespace(self, namespace: impl Into<String>) -> Self
The Vault Enterprise namespace, if there is one.
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.
It bounds each request watch makes — the metadata
check, and the read that follows a version change — not the loop,
which runs until it is stopped.
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 metadata poll is failing,
whose Vault is sealed or whose 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 a Vault that
stopped answering an hour ago would look healthy until something called
refresh_remote.
That gap is wider here than anywhere else in this family, because this watch is a poll of a version counter: a secret that has not changed is never read, so a healthy Vault and a Vault whose token expired yesterday deliver exactly the same thing — nothing.
let sink = DbConfig::remote_sink();
Vault::new(address, "secret", "myapp/db")
.with_token(std::env::var("VAULT_TOKEN").unwrap())
.reporting_to(sink)
.watch(&watching, Duration::from_secs(30), 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,
interval: Duration,
on_change: F,
) -> Result<(), Error>
pub fn watch<F>( &self, watching: &Watching, interval: Duration, on_change: F, ) -> Result<(), Error>
Calls on_change when the secret’s version moves, checking every
interval.
Polling, because Vault offers nothing better — and metadata polling,
because reading a secret every thirty seconds to discover it has not
changed is a poor thing to do to a secrets store. Each tick reads
{mount}/metadata/{path} for current_version; only a new version
triggers a read of the secret itself.
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(vault.fetch()?)?;
vault.watch(&watching, Duration::from_secs(30), move |document| sink.apply(document))A failed check does not end the watch — an expired token, a sealed
Vault, a network blip — it waits out the interval and tries again. stop
is noticed within a quarter second regardless of how long interval is.
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 failing for an hour stops
reporting the store as healthy.
§Errors
If the source reads several paths: the counter this polls belongs to
one secret, and a set of secrets has none of its own. Or if the mount
turns out not to be KV v2: a v1 mount has no version counter, so every
tick would find “no change” and the watch would silently never fire — a
misconfiguration, reported as one. Or 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.
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for Vault
impl !RefUnwindSafe for Vault
impl !UnwindSafe for Vault
impl Send for Vault
impl Sync for Vault
impl Unpin for Vault
impl UnsafeUnpin for Vault
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);