Change-Id: I75d8d61f836c76e8765e0e3b49022c056de84850 Reviewed-on: https://cl.tvl.fyi/c/depot/+/11466 Reviewed-by: Connor Brewster <cbrewster@hey.com> Autosubmit: flokli <flokli@flokli.de> Tested-by: BuildkiteCI
90 lines
3.6 KiB
Rust
90 lines
3.6 KiB
Rust
use super::{grpc::GRPCBuildService, BuildService, DummyBuildService};
|
|
use tvix_castore::{blobservice::BlobService, directoryservice::DirectoryService};
|
|
use url::Url;
|
|
|
|
/// Constructs a new instance of a [BuildService] from an URI.
|
|
///
|
|
/// The following schemes are supported by the following services:
|
|
/// - `dummy://` ([DummyBuildService])
|
|
/// - `grpc+*://` ([GRPCBuildService])
|
|
///
|
|
/// As some of these [BuildService] need to talk to a [BlobService] and
|
|
/// [DirectoryService], these also need to be passed in.
|
|
pub async fn from_addr<BS, DS>(
|
|
uri: &str,
|
|
_blob_service: BS,
|
|
_directory_service: DS,
|
|
) -> std::io::Result<Box<dyn BuildService>>
|
|
where
|
|
BS: AsRef<dyn BlobService> + Send + Sync + Clone + 'static,
|
|
DS: AsRef<dyn DirectoryService> + Send + Sync + Clone + 'static,
|
|
{
|
|
let url = Url::parse(uri)
|
|
.map_err(|e| std::io::Error::other(format!("unable to parse url: {}", e)))?;
|
|
|
|
Ok(match url.scheme() {
|
|
// dummy doesn't care about parameters.
|
|
"dummy" => Box::<DummyBuildService>::default(),
|
|
scheme => {
|
|
if scheme.starts_with("grpc+") {
|
|
let client = crate::proto::build_service_client::BuildServiceClient::new(
|
|
tvix_castore::tonic::channel_from_url(&url)
|
|
.await
|
|
.map_err(std::io::Error::other)?,
|
|
);
|
|
// FUTUREWORK: also allow responding to {blob,directory}_service
|
|
// requests from the remote BuildService?
|
|
Box::new(GRPCBuildService::from_client(client))
|
|
} else {
|
|
Err(std::io::Error::other(format!(
|
|
"unknown scheme: {}",
|
|
url.scheme()
|
|
)))?
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::sync::Arc;
|
|
|
|
use super::from_addr;
|
|
use rstest::rstest;
|
|
use tvix_castore::{
|
|
blobservice::{BlobService, MemoryBlobService},
|
|
directoryservice::{DirectoryService, MemoryDirectoryService},
|
|
};
|
|
|
|
#[rstest]
|
|
/// This uses an unsupported scheme.
|
|
#[case::unsupported_scheme("http://foo.example/test", false)]
|
|
/// This configures dummy
|
|
#[case::valid_dummy("dummy://", true)]
|
|
/// Correct scheme to connect to a unix socket.
|
|
#[case::grpc_valid_unix_socket("grpc+unix:///path/to/somewhere", true)]
|
|
/// Correct scheme for unix socket, but setting a host too, which is invalid.
|
|
#[case::grpc_invalid_unix_socket_and_host("grpc+unix://host.example/path/to/somewhere", false)]
|
|
/// Correct scheme to connect to localhost, with port 12345
|
|
#[case::grpc_valid_ipv6_localhost_port_12345("grpc+http://[::1]:12345", true)]
|
|
/// Correct scheme to connect to localhost over http, without specifying a port.
|
|
#[case::grpc_valid_http_host_without_port("grpc+http://localhost", true)]
|
|
/// Correct scheme to connect to localhost over http, without specifying a port.
|
|
#[case::grpc_valid_https_host_without_port("grpc+https://localhost", true)]
|
|
/// Correct scheme to connect to localhost over http, but with additional path, which is invalid.
|
|
#[case::grpc_invalid_host_and_path("grpc+http://localhost/some-path", false)]
|
|
#[tokio::test]
|
|
async fn test_from_addr(#[case] uri_str: &str, #[case] exp_succeed: bool) {
|
|
let blob_service: Arc<dyn BlobService> = Arc::from(MemoryBlobService::default());
|
|
let directory_service: Arc<dyn DirectoryService> =
|
|
Arc::from(MemoryDirectoryService::default());
|
|
|
|
let resp = from_addr(uri_str, blob_service, directory_service).await;
|
|
|
|
if exp_succeed {
|
|
resp.expect("should succeed");
|
|
} else {
|
|
assert!(resp.is_err(), "should fail");
|
|
}
|
|
}
|
|
}
|