pub struct CanopyClient { /* private fields */ }Expand description
HTTP client with auth configured for talking to a canopy server.
Tries two auth paths in order of preference:
- Tailscale: if the canopy tailnet endpoint is reachable, plain HTTPS works (auth is implicit via tailscale identity).
- mTLS: a fresh self-signed cert from the device key, short-lived
([
CERT_VALIDITY_DAYS]); for long-running daemons,Self::renewshould tick onCERT_RENEW_AFTERto swap in a fresh cert before expiry.
Self::refresh re-probes tailscale and swaps modes on reload.
Implementations§
Source§impl CanopyClient
impl CanopyClient
Sourcepub async fn new(
tamanu_version: impl Into<String>,
device_key_pem: Option<&str>,
make_builder: impl Fn() -> ClientBuilder + Send + Sync + 'static,
) -> Result<Option<Self>>
pub async fn new( tamanu_version: impl Into<String>, device_key_pem: Option<&str>, make_builder: impl Fn() -> ClientBuilder + Send + Sync + 'static, ) -> Result<Option<Self>>
Build a canopy client, preferring tailscale and falling back to mTLS.
Probes the tailscale canopy endpoint first; if reachable, uses it.
Otherwise, if a device key PEM is provided, builds an mTLS client.
Returns Ok(None) if neither path is available.
tamanu_version is the version of the Tamanu install this client
speaks for; sent on every request via the X-Version header.
make_builder supplies the base reqwest::ClientBuilder — see
ClientBuilderFactory. Use client_builder for a sensible default.
Sourcepub async fn is_tailscale(&self) -> bool
pub async fn is_tailscale(&self) -> bool
Returns true if the client is currently using the tailscale path.
Sourcepub async fn refresh(&self) -> Result<()>
pub async fn refresh(&self) -> Result<()>
Re-probe tailscale and swap modes if the picture has changed.
Intended to be called when the daemon receives a reload signal.
Sourcepub async fn renew(&self) -> Result<()>
pub async fn renew(&self) -> Result<()>
Rebuild the underlying HTTP client with a fresh certificate.
No-op in tailscale mode (no cert to rotate). In mTLS mode, atomically replaces the live client; in-flight requests continue with the old client until they complete.
Sourcepub async fn post_status(
&self,
base_url: &Url,
server_id: &str,
payload: &Value,
) -> Result<Vec<String>>
pub async fn post_status( &self, base_url: &Url, server_id: &str, payload: &Value, ) -> Result<Vec<String>>
POST a status snapshot to the canopy server.
In tailscale mode, base_url is ignored and a {TAILSCALE_URL}/public/status/{server_id}
URL is used. In mTLS mode, posts to {base_url}/status/{server_id}.
The payload is free-form JSON; the canopy /status contract reserves the
top-level health: [] key, whose entries each carry a result of
passed | warning | failed | broken | skipped. The body is gzip-encoded
with Content-Encoding: gzip.
Returns backup_now: the backup-type names canopy says this server should
back up right now (operator one-offs + schedule-due). Empty means nothing
to do. A response that predates the field (no backup_now) yields an empty
list, so older canopy deployments keep working.
Sourcepub async fn get(
&self,
base_url: &Url,
tailscale_path: &str,
mtls_path: &str,
) -> Result<Response>
pub async fn get( &self, base_url: &Url, tailscale_path: &str, mtls_path: &str, ) -> Result<Response>
GET a path on the canopy server, routed via tailscale when available.
In tailscale mode, the request goes to {TAILSCALE_URL}{tailscale_path}
(typically /public/..., the only mount that accepts tagged-device
tailscale callers). In mTLS mode, the request goes to {base_url}{mtls_path}.
Returns the raw response — the caller is responsible for status checks and body parsing so they can choose how to fall back if the response isn’t usable.
Sourcepub async fn post_event(
&self,
base_url: &Url,
event: NewEvent<'_>,
) -> Result<()>
pub async fn post_event( &self, base_url: &Url, event: NewEvent<'_>, ) -> Result<()>
POST an event to the canopy server.
In tailscale mode, base_url is ignored and TAILSCALE_URL is used.
In mTLS mode, posts to {base_url}/events.
Sourcepub async fn request(
&self,
method: Method,
base_url: &Url,
path: &str,
) -> Result<RequestBuilder>
pub async fn request( &self, method: Method, base_url: &Url, path: &str, ) -> Result<RequestBuilder>
Start a request to an arbitrary canopy endpoint on the current auth path.
This is the generic escape hatch behind the typed endpoint methods: it
resolves the right HTTP client and URL for the active auth mode, begins
the request, and sets the X-Version header. The returned builder is
yours to finish — add query params, a body, or extra headers, then
.send() and parse the response however suits.
path is the mTLS-mode path (e.g. /backup-target); over tailscale the
same endpoint is mounted under /public, so this routes it there, the
same convention the other endpoint methods follow.
Sourcepub async fn request_json<Res: DeserializeOwned>(
&self,
method: Method,
base_url: &Url,
path: &str,
body: Option<&(impl Serialize + ?Sized)>,
) -> Result<Res>
pub async fn request_json<Res: DeserializeOwned>( &self, method: Method, base_url: &Url, path: &str, body: Option<&(impl Serialize + ?Sized)>, ) -> Result<Res>
Call an arbitrary canopy endpoint and parse its JSON response.
Builds the request via Self::request, attaches body as JSON when
it’s Some, sends it, and on a 2xx response parses the body into Res.
A non-success status becomes an error carrying the status and response
body, matching the other endpoint methods. This absorbs the status-check
and parse boilerplate.
Use serde_json::Value for Res (and/or the body) for fully dynamic
calls, or any concrete type for typed calls. When passing no body, pin
the inference with a turbofish, e.g. None::<&()>.
path follows the same mTLS/tailscale convention as Self::request.
Sourcepub async fn backup_capabilities(
&self,
base_url: &Url,
types: &[String],
) -> Result<()>
pub async fn backup_capabilities( &self, base_url: &Url, types: &[String], ) -> Result<()>
Register the backup types this server can run (POST /backup-capabilities).
Sourcepub async fn backup_credentials(
&self,
base_url: &Url,
backup_type: &str,
purpose: Purpose,
) -> Result<BackupCredentials>
pub async fn backup_credentials( &self, base_url: &Url, backup_type: &str, purpose: Purpose, ) -> Result<BackupCredentials>
Obtain short-lived S3 credentials for a backup type (POST /backup-credentials).
Returns the credential_process-shaped creds; the caller translates them
to the container-creds shape for kopia. 412/409/502 surface as errors.
Sourcepub async fn backup_target(&self, base_url: &Url) -> Result<TargetOutcome>
pub async fn backup_target(&self, base_url: &Url) -> Result<TargetOutcome>
Fetch the S3 repo target (GET /backup-target).
412/409 mean the device isn’t yet authorised for backups; these map to
TargetOutcome::Dormant (a benign idle state) rather than an error.
Sourcepub async fn backup_report(
&self,
base_url: &Url,
report: &BackupReport<'_>,
) -> Result<()>
pub async fn backup_report( &self, base_url: &Url, report: &BackupReport<'_>, ) -> Result<()>
Report a completed backup/restore run (POST /backup-report).
Sourcepub async fn restore_capabilities(
&self,
base_url: &Url,
intents: &[&str],
) -> Result<()>
pub async fn restore_capabilities( &self, base_url: &Url, intents: &[&str], ) -> Result<()>
Register the restore intents this consumer supports (POST /restore-capabilities).
Replaces the registered intent set wholesale. Canopy dispatches only matching worklist entries.
Sourcepub async fn restore_worklist(
&self,
base_url: &Url,
) -> Result<Vec<WorklistEntry>>
pub async fn restore_worklist( &self, base_url: &Url, ) -> Result<Vec<WorklistEntry>>
Fetch the desired-state worklist of replicas to restore (GET /restore-worklist).
Sourcepub async fn restore_credentials(
&self,
base_url: &Url,
backup_type: &str,
group: Uuid,
) -> Result<RestoreCredentials>
pub async fn restore_credentials( &self, base_url: &Url, backup_type: &str, group: Uuid, ) -> Result<RestoreCredentials>
Obtain read-only S3 credentials and the repo password for a group
(POST /restore-credentials).
Creds are 1-hour chained STS; refresh by re-calling. 403/409/502
surface as errors.
Sourcepub async fn restore_verification(
&self,
base_url: &Url,
report: &RestoreVerification<'_>,
) -> Result<()>
pub async fn restore_verification( &self, base_url: &Url, report: &RestoreVerification<'_>, ) -> Result<()>
Report a restore’s health (POST /restore-verification).
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for CanopyClient
impl !RefUnwindSafe for CanopyClient
impl !UnwindSafe for CanopyClient
impl Send for CanopyClient
impl Sync for CanopyClient
impl Unpin for CanopyClient
impl UnsafeUnpin for CanopyClient
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
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<D> OwoColorize for D
impl<D> OwoColorize for D
Source§fn fg<C>(&self) -> FgColorDisplay<'_, C, Self>where
C: Color,
fn fg<C>(&self) -> FgColorDisplay<'_, C, Self>where
C: Color,
Source§fn bg<C>(&self) -> BgColorDisplay<'_, C, Self>where
C: Color,
fn bg<C>(&self) -> BgColorDisplay<'_, C, Self>where
C: Color,
Source§fn black(&self) -> FgColorDisplay<'_, Black, Self>
fn black(&self) -> FgColorDisplay<'_, Black, Self>
Source§fn on_black(&self) -> BgColorDisplay<'_, Black, Self>
fn on_black(&self) -> BgColorDisplay<'_, Black, Self>
Source§fn red(&self) -> FgColorDisplay<'_, Red, Self>
fn red(&self) -> FgColorDisplay<'_, Red, Self>
Source§fn on_red(&self) -> BgColorDisplay<'_, Red, Self>
fn on_red(&self) -> BgColorDisplay<'_, Red, Self>
Source§fn green(&self) -> FgColorDisplay<'_, Green, Self>
fn green(&self) -> FgColorDisplay<'_, Green, Self>
Source§fn on_green(&self) -> BgColorDisplay<'_, Green, Self>
fn on_green(&self) -> BgColorDisplay<'_, Green, Self>
Source§fn yellow(&self) -> FgColorDisplay<'_, Yellow, Self>
fn yellow(&self) -> FgColorDisplay<'_, Yellow, Self>
Source§fn on_yellow(&self) -> BgColorDisplay<'_, Yellow, Self>
fn on_yellow(&self) -> BgColorDisplay<'_, Yellow, Self>
Source§fn blue(&self) -> FgColorDisplay<'_, Blue, Self>
fn blue(&self) -> FgColorDisplay<'_, Blue, Self>
Source§fn on_blue(&self) -> BgColorDisplay<'_, Blue, Self>
fn on_blue(&self) -> BgColorDisplay<'_, Blue, Self>
Source§fn magenta(&self) -> FgColorDisplay<'_, Magenta, Self>
fn magenta(&self) -> FgColorDisplay<'_, Magenta, Self>
Source§fn on_magenta(&self) -> BgColorDisplay<'_, Magenta, Self>
fn on_magenta(&self) -> BgColorDisplay<'_, Magenta, Self>
Source§fn purple(&self) -> FgColorDisplay<'_, Magenta, Self>
fn purple(&self) -> FgColorDisplay<'_, Magenta, Self>
Source§fn on_purple(&self) -> BgColorDisplay<'_, Magenta, Self>
fn on_purple(&self) -> BgColorDisplay<'_, Magenta, Self>
Source§fn cyan(&self) -> FgColorDisplay<'_, Cyan, Self>
fn cyan(&self) -> FgColorDisplay<'_, Cyan, Self>
Source§fn on_cyan(&self) -> BgColorDisplay<'_, Cyan, Self>
fn on_cyan(&self) -> BgColorDisplay<'_, Cyan, Self>
Source§fn white(&self) -> FgColorDisplay<'_, White, Self>
fn white(&self) -> FgColorDisplay<'_, White, Self>
Source§fn on_white(&self) -> BgColorDisplay<'_, White, Self>
fn on_white(&self) -> BgColorDisplay<'_, White, Self>
Source§fn default_color(&self) -> FgColorDisplay<'_, Default, Self>
fn default_color(&self) -> FgColorDisplay<'_, Default, Self>
Source§fn on_default_color(&self) -> BgColorDisplay<'_, Default, Self>
fn on_default_color(&self) -> BgColorDisplay<'_, Default, Self>
Source§fn bright_black(&self) -> FgColorDisplay<'_, BrightBlack, Self>
fn bright_black(&self) -> FgColorDisplay<'_, BrightBlack, Self>
Source§fn on_bright_black(&self) -> BgColorDisplay<'_, BrightBlack, Self>
fn on_bright_black(&self) -> BgColorDisplay<'_, BrightBlack, Self>
Source§fn bright_red(&self) -> FgColorDisplay<'_, BrightRed, Self>
fn bright_red(&self) -> FgColorDisplay<'_, BrightRed, Self>
Source§fn on_bright_red(&self) -> BgColorDisplay<'_, BrightRed, Self>
fn on_bright_red(&self) -> BgColorDisplay<'_, BrightRed, Self>
Source§fn bright_green(&self) -> FgColorDisplay<'_, BrightGreen, Self>
fn bright_green(&self) -> FgColorDisplay<'_, BrightGreen, Self>
Source§fn on_bright_green(&self) -> BgColorDisplay<'_, BrightGreen, Self>
fn on_bright_green(&self) -> BgColorDisplay<'_, BrightGreen, Self>
Source§fn bright_yellow(&self) -> FgColorDisplay<'_, BrightYellow, Self>
fn bright_yellow(&self) -> FgColorDisplay<'_, BrightYellow, Self>
Source§fn on_bright_yellow(&self) -> BgColorDisplay<'_, BrightYellow, Self>
fn on_bright_yellow(&self) -> BgColorDisplay<'_, BrightYellow, Self>
Source§fn bright_blue(&self) -> FgColorDisplay<'_, BrightBlue, Self>
fn bright_blue(&self) -> FgColorDisplay<'_, BrightBlue, Self>
Source§fn on_bright_blue(&self) -> BgColorDisplay<'_, BrightBlue, Self>
fn on_bright_blue(&self) -> BgColorDisplay<'_, BrightBlue, Self>
Source§fn bright_magenta(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
fn bright_magenta(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
Source§fn on_bright_magenta(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
fn on_bright_magenta(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
Source§fn bright_purple(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
fn bright_purple(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
Source§fn on_bright_purple(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
fn on_bright_purple(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
Source§fn bright_cyan(&self) -> FgColorDisplay<'_, BrightCyan, Self>
fn bright_cyan(&self) -> FgColorDisplay<'_, BrightCyan, Self>
Source§fn on_bright_cyan(&self) -> BgColorDisplay<'_, BrightCyan, Self>
fn on_bright_cyan(&self) -> BgColorDisplay<'_, BrightCyan, Self>
Source§fn bright_white(&self) -> FgColorDisplay<'_, BrightWhite, Self>
fn bright_white(&self) -> FgColorDisplay<'_, BrightWhite, Self>
Source§fn on_bright_white(&self) -> BgColorDisplay<'_, BrightWhite, Self>
fn on_bright_white(&self) -> BgColorDisplay<'_, BrightWhite, Self>
Source§fn bold(&self) -> BoldDisplay<'_, Self>
fn bold(&self) -> BoldDisplay<'_, Self>
Source§fn dimmed(&self) -> DimDisplay<'_, Self>
fn dimmed(&self) -> DimDisplay<'_, Self>
Source§fn italic(&self) -> ItalicDisplay<'_, Self>
fn italic(&self) -> ItalicDisplay<'_, Self>
Source§fn underline(&self) -> UnderlineDisplay<'_, Self>
fn underline(&self) -> UnderlineDisplay<'_, Self>
Source§fn blink(&self) -> BlinkDisplay<'_, Self>
fn blink(&self) -> BlinkDisplay<'_, Self>
Source§fn blink_fast(&self) -> BlinkFastDisplay<'_, Self>
fn blink_fast(&self) -> BlinkFastDisplay<'_, Self>
Source§fn reversed(&self) -> ReversedDisplay<'_, Self>
fn reversed(&self) -> ReversedDisplay<'_, Self>
Source§fn strikethrough(&self) -> StrikeThroughDisplay<'_, Self>
fn strikethrough(&self) -> StrikeThroughDisplay<'_, Self>
Source§fn color<Color>(&self, color: Color) -> FgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
fn color<Color>(&self, color: Color) -> FgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
OwoColorize::fg or
a color-specific method, such as OwoColorize::green, Read moreSource§fn on_color<Color>(&self, color: Color) -> BgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
fn on_color<Color>(&self, color: Color) -> BgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
OwoColorize::bg or
a color-specific method, such as OwoColorize::on_yellow, Read more