#[cfg(feature = "x509")]
mod x509;
mod header;
#[cfg(feature = "jwt")]
mod jwt;
use crate::transport::connect;
use crate::transport::Endpoint;
use crate::workload_api::client::header::MetadataAdder;
use crate::workload_api::error::WorkloadApiError;
use crate::workload_api::pb::workload::spiffe_workload_api_client::SpiffeWorkloadApiClient;
#[cfg(all(test, feature = "jwt"))]
use crate::{JwtSvid, SpiffeId};
#[cfg(all(test, feature = "jwt"))]
use std::{future::Future, pin::Pin, sync::Arc};
pub use header::InterceptorFn;
#[cfg(all(test, feature = "jwt"))]
pub(crate) type JwtFetchTestHook = Arc<
dyn Fn(
Vec<String>,
Option<SpiffeId>,
) -> Pin<Box<dyn Future<Output = Result<JwtSvid, WorkloadApiError>> + Send>>
+ Send
+ Sync
+ 'static,
>;
#[cfg_attr(not(all(test, feature = "jwt")), derive(Debug))]
#[derive(Clone)]
pub struct WorkloadApiClient {
endpoint: Endpoint,
client: SpiffeWorkloadApiClient<
tonic::service::interceptor::InterceptedService<tonic::transport::Channel, MetadataAdder>,
>,
#[cfg(all(test, feature = "jwt"))]
jwt_fetch_hook: Option<JwtFetchTestHook>,
}
#[cfg(all(test, feature = "jwt"))]
impl std::fmt::Debug for WorkloadApiClient {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WorkloadApiClient")
.field("endpoint", &self.endpoint)
.field("client", &self.client)
.finish_non_exhaustive()
}
}
impl WorkloadApiClient {
pub const fn endpoint(&self) -> &Endpoint {
&self.endpoint
}
pub async fn connect(endpoint: Endpoint) -> Result<Self, WorkloadApiError> {
let channel = connect(&endpoint).await?;
Ok(Self {
endpoint,
client: SpiffeWorkloadApiClient::with_interceptor(channel, MetadataAdder::new(None)),
#[cfg(all(test, feature = "jwt"))]
jwt_fetch_hook: None,
})
}
pub async fn connect_to(endpoint: impl AsRef<str>) -> Result<Self, WorkloadApiError> {
let endpoint = Endpoint::parse(endpoint.as_ref())?;
Self::connect(endpoint).await
}
pub async fn connect_env() -> Result<Self, WorkloadApiError> {
let endpoint = crate::workload_api::endpoint::from_env()?;
Self::connect(endpoint).await
}
pub fn new_with_channel(endpoint: Endpoint, channel: tonic::transport::Channel) -> Self {
Self {
endpoint,
client: SpiffeWorkloadApiClient::with_interceptor(channel, MetadataAdder::new(None)),
#[cfg(all(test, feature = "jwt"))]
jwt_fetch_hook: None,
}
}
pub fn new_with_channel_and_interceptor(
endpoint: Endpoint,
channel: tonic::transport::Channel,
interceptor: InterceptorFn,
) -> Self {
Self {
endpoint,
client: SpiffeWorkloadApiClient::with_interceptor(
channel,
MetadataAdder::new(Some(interceptor)),
),
#[cfg(all(test, feature = "jwt"))]
jwt_fetch_hook: None,
}
}
#[cfg(all(test, feature = "jwt"))]
pub(crate) fn new_with_jwt_fetch_hook(endpoint: Endpoint, hook: JwtFetchTestHook) -> Self {
let channel = tonic::transport::Endpoint::from_static("http://127.0.0.1:1").connect_lazy();
Self {
endpoint,
client: SpiffeWorkloadApiClient::with_interceptor(channel, MetadataAdder::new(None)),
jwt_fetch_hook: Some(hook),
}
}
}
impl WorkloadApiClient {
async fn first_message<T>(mut stream: tonic::Streaming<T>) -> Result<T, WorkloadApiError> {
stream
.message()
.await?
.ok_or(WorkloadApiError::EmptyResponse)
}
}