pub mod generated;
pub mod services;
pub mod session;
pub mod types;
use foundation_connectrpc::{
Client, ClientOptions, Ctx, H2Transport, ProcedureCodecs, Request,
ServerStream, Transport,
};
use std::path::Path;
use std::sync::Arc;
use crate::buildkit::types::{
BuildHistoryEvent, BuildHistoryRequest, DiskUsageRequest, DiskUsageResponse, InfoRequest,
InfoResponse, ListWorkersRequest, ListWorkersResponse, PruneRequest, SolveRequest,
SolveResponse, StatusRequest, StatusResponse, UpdateBuildHistoryRequest,
UpdateBuildHistoryResponse, UsageRecord,
};
pub struct BuildKitClient {
pub transport: Arc<dyn Transport>,
authority: String,
pub solve: Client<SolveRequest, SolveResponse>,
pub status: Client<StatusRequest, StatusResponse>,
pub info: Client<InfoRequest, InfoResponse>,
pub disk_usage: Client<DiskUsageRequest, DiskUsageResponse>,
pub prune: Client<PruneRequest, UsageRecord>,
pub list_workers: Client<ListWorkersRequest, ListWorkersResponse>,
pub listen_build_history: Client<BuildHistoryRequest, BuildHistoryEvent>,
pub update_build_history: Client<UpdateBuildHistoryRequest, UpdateBuildHistoryResponse>,
}
#[must_use]
pub fn new_build_ref() -> String {
session::new_session_id()
}
impl BuildKitClient {
pub fn connect(socket_path: impl AsRef<Path>) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let transport: Arc<dyn Transport> = Arc::new(H2Transport::unix(socket_path.as_ref()));
Self::from_transport(transport, "localhost")
}
pub fn connect_tcp(authority: &str) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let transport: Arc<dyn Transport> = Arc::new(H2Transport::new());
Self::from_transport(transport, authority)
}
#[must_use]
pub fn transport(&self) -> &Arc<dyn Transport> {
&self.transport
}
fn from_transport(
transport: Arc<dyn Transport>,
authority: &str,
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let base_url = format!("http://{authority}/moby.buildkit.v1.Control");
let base_url = base_url.as_str();
let opts = || ClientOptions::new().with_grpc();
let info = Client::<InfoRequest, InfoResponse>::new(
Arc::clone(&transport),
&format!("{base_url}/Info"),
ProcedureCodecs::defaults(),
opts(),
)?;
let status = Client::<StatusRequest, StatusResponse>::new(
Arc::clone(&transport),
&format!("{base_url}/Status"),
ProcedureCodecs::defaults(),
opts(),
)?;
let solve = Client::<SolveRequest, SolveResponse>::new(
Arc::clone(&transport),
&format!("{base_url}/Solve"),
ProcedureCodecs::defaults(),
opts(),
)?;
let disk_usage = Client::<DiskUsageRequest, DiskUsageResponse>::new(
Arc::clone(&transport),
&format!("{base_url}/DiskUsage"),
ProcedureCodecs::defaults(),
opts(),
)?;
let prune = Client::<PruneRequest, UsageRecord>::new(
Arc::clone(&transport),
&format!("{base_url}/Prune"),
ProcedureCodecs::defaults(),
opts(),
)?;
let list_workers = Client::<ListWorkersRequest, ListWorkersResponse>::new(
Arc::clone(&transport),
&format!("{base_url}/ListWorkers"),
ProcedureCodecs::defaults(),
opts(),
)?;
let listen_build_history = Client::<BuildHistoryRequest, BuildHistoryEvent>::new(
Arc::clone(&transport),
&format!("{base_url}/ListenBuildHistory"),
ProcedureCodecs::defaults(),
opts(),
)?;
let update_build_history =
Client::<UpdateBuildHistoryRequest, UpdateBuildHistoryResponse>::new(
Arc::clone(&transport),
&format!("{base_url}/UpdateBuildHistory"),
ProcedureCodecs::defaults(),
opts(),
)?;
Ok(Self {
transport,
authority: authority.to_string(),
solve,
status,
info,
disk_usage,
prune,
list_workers,
listen_build_history,
update_build_history,
})
}
pub fn gateway_for_build(
&self,
build_ref: impl Into<String>,
) -> Result<services::gateway::LLBBridgeClient, Box<dyn std::error::Error + Send + Sync>>
{
let client = services::gateway::LLBBridgeClient::new(
Arc::clone(&self.transport),
&format!("http://{}", self.authority),
ClientOptions::new()
.with_grpc()
.with_header("buildkit-controlapi-buildid".to_string(), build_ref.into()),
)?;
Ok(client)
}
pub async fn info(&self) -> Result<InfoResponse, Box<dyn std::error::Error + Send + Sync>> {
let ctx = Ctx::background();
let response = self.info.unary(ctx, Request::new(InfoRequest::default())).await?;
Ok(response.msg)
}
pub async fn solve(
&self,
mut request: SolveRequest,
) -> Result<SolveResponse, Box<dyn std::error::Error + Send + Sync>> {
if request.Ref.is_empty() {
request.Ref = new_build_ref();
}
let ctx = Ctx::background();
let response = self.solve.unary(ctx, Request::new(request)).await?;
Ok(response.msg)
}
pub async fn status(
&self,
build_ref: impl Into<String>,
) -> Result<ServerStream<StatusResponse>, Box<dyn std::error::Error + Send + Sync>> {
let ctx = Ctx::background();
let request = StatusRequest { Ref: build_ref.into(), ..Default::default() };
let stream = self.status.server_stream(ctx, Request::new(request)).await?;
Ok(stream)
}
pub async fn disk_usage(
&self,
filters: Vec<String>,
) -> Result<DiskUsageResponse, Box<dyn std::error::Error + Send + Sync>> {
let ctx = Ctx::background();
let request = DiskUsageRequest { filter: filters, ..Default::default() };
let response = self.disk_usage.unary(ctx, Request::new(request)).await?;
Ok(response.msg)
}
pub async fn list_workers(
&self,
filters: Vec<String>,
) -> Result<ListWorkersResponse, Box<dyn std::error::Error + Send + Sync>> {
let ctx = Ctx::background();
let request = ListWorkersRequest { filter: filters, ..Default::default() };
let response = self.list_workers.unary(ctx, Request::new(request)).await?;
Ok(response.msg)
}
pub async fn prune(
&self,
all: bool,
keep_bytes: i64,
filters: Vec<String>,
) -> Result<ServerStream<UsageRecord>, Box<dyn std::error::Error + Send + Sync>> {
let ctx = Ctx::background();
let request = PruneRequest {
all,
reservedSpace: keep_bytes,
filter: filters,
..Default::default()
};
let stream = self.prune.server_stream(ctx, Request::new(request)).await?;
Ok(stream)
}
pub async fn listen_build_history(
&self,
build_ref: impl Into<String>,
early_exit: bool,
) -> Result<ServerStream<BuildHistoryEvent>, Box<dyn std::error::Error + Send + Sync>> {
let ctx = Ctx::background();
let request = BuildHistoryRequest {
Ref: build_ref.into(),
EarlyExit: early_exit,
..Default::default()
};
let stream = self
.listen_build_history
.server_stream(ctx, Request::new(request))
.await?;
Ok(stream)
}
pub async fn update_build_history(
&self,
build_ref: impl Into<String>,
pinned: bool,
delete: bool,
) -> Result<UpdateBuildHistoryResponse, Box<dyn std::error::Error + Send + Sync>> {
let ctx = Ctx::background();
let request = UpdateBuildHistoryRequest {
Ref: build_ref.into(),
Pinned: pinned,
Delete: delete,
..Default::default()
};
let response = self
.update_build_history
.unary(ctx, Request::new(request))
.await?;
Ok(response.msg)
}
}