From 170e0cdfadd96176595c47b573d0a1f27a7c734b Mon Sep 17 00:00:00 2001 From: Florian Klink Date: Mon, 15 Jan 2024 19:15:45 +0200 Subject: feat(tvix/build): add from_addr method This allows constructing a BuildService from a URI, similar to how it's done in tvix-[ca]store. Change-Id: Ib962b329535c6c7e378ab7ac7f4dd254366497b3 Reviewed-on: https://cl.tvl.fyi/c/depot/+/10630 Tested-by: BuildkiteCI Reviewed-by: raitobezarius Autosubmit: flokli --- tvix/build/src/buildservice/from_addr.rs | 85 ++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 tvix/build/src/buildservice/from_addr.rs (limited to 'tvix/build/src/buildservice/from_addr.rs') diff --git a/tvix/build/src/buildservice/from_addr.rs b/tvix/build/src/buildservice/from_addr.rs new file mode 100644 index 000000000000..ee2b4e50b4da --- /dev/null +++ b/tvix/build/src/buildservice/from_addr.rs @@ -0,0 +1,85 @@ +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( + uri: &str, + _blob_service: BS, + _directory_service: DS, +) -> std::io::Result> +where + BS: AsRef + Send + Sync + Clone + 'static, + DS: AsRef + 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::::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 test_case::test_case; + use tvix_castore::utils::{gen_blob_service, gen_directory_service}; + + /// This uses an unsupported scheme. + #[test_case("http://foo.example/test", false; "unsupported scheme")] + /// This configures dummy + #[test_case("dummy://", true; "valid dummy")] + /// Correct scheme to connect to a unix socket. + #[test_case("grpc+unix:///path/to/somewhere", true; "grpc valid unix socket")] + /// Correct scheme for unix socket, but setting a host too, which is invalid. + #[test_case("grpc+unix://host.example/path/to/somewhere", false; "grpc invalid unix socket and host")] + /// Correct scheme to connect to localhost, with port 12345 + #[test_case("grpc+http://[::1]:12345", true; "grpc valid IPv6 localhost port 12345")] + /// Correct scheme to connect to localhost over http, without specifying a port. + #[test_case("grpc+http://localhost", true; "grpc valid http host without port")] + /// Correct scheme to connect to localhost over http, without specifying a port. + #[test_case("grpc+https://localhost", true; "grpc valid https host without port")] + /// Correct scheme to connect to localhost over http, but with additional path, which is invalid. + #[test_case("grpc+http://localhost/some-path", false; "grpc valid invalid host and path")] + #[tokio::test] + async fn test_from_addr(uri_str: &str, is_ok: bool) { + assert_eq!( + from_addr( + uri_str, + Arc::from(gen_blob_service()), + Arc::from(gen_directory_service()) + ) + .await + .is_ok(), + is_ok + ) + } +} -- cgit 1.4.1