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 <tvl@lahfa.xyz> Autosubmit: flokli <flokli@flokli.de>
This commit is contained in:
parent
5aa9b29d8c
commit
170e0cdfad
5 changed files with 99 additions and 0 deletions
2
tvix/Cargo.lock
generated
2
tvix/Cargo.lock
generated
|
@ -3288,11 +3288,13 @@ dependencies = [
|
||||||
"prost-build",
|
"prost-build",
|
||||||
"test-case",
|
"test-case",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
"tonic 0.10.2",
|
"tonic 0.10.2",
|
||||||
"tonic-build",
|
"tonic-build",
|
||||||
"tonic-reflection",
|
"tonic-reflection",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tvix-castore",
|
"tvix-castore",
|
||||||
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -10237,6 +10237,10 @@ rec {
|
||||||
name = "thiserror";
|
name = "thiserror";
|
||||||
packageId = "thiserror";
|
packageId = "thiserror";
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
name = "tokio";
|
||||||
|
packageId = "tokio";
|
||||||
|
}
|
||||||
{
|
{
|
||||||
name = "tonic";
|
name = "tonic";
|
||||||
packageId = "tonic 0.10.2";
|
packageId = "tonic 0.10.2";
|
||||||
|
@ -10255,6 +10259,10 @@ rec {
|
||||||
name = "tvix-castore";
|
name = "tvix-castore";
|
||||||
packageId = "tvix-castore";
|
packageId = "tvix-castore";
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
name = "url";
|
||||||
|
packageId = "url";
|
||||||
|
}
|
||||||
];
|
];
|
||||||
buildDependencies = [
|
buildDependencies = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,9 +8,11 @@ bytes = "1.4.0"
|
||||||
itertools = "0.12.0"
|
itertools = "0.12.0"
|
||||||
prost = "0.12.1"
|
prost = "0.12.1"
|
||||||
thiserror = "1.0.56"
|
thiserror = "1.0.56"
|
||||||
|
tokio = { version = "1.32.0" }
|
||||||
tonic = { version = "0.10.2", features = ["tls", "tls-roots"] }
|
tonic = { version = "0.10.2", features = ["tls", "tls-roots"] }
|
||||||
tvix-castore = { path = "../castore" }
|
tvix-castore = { path = "../castore" }
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.37"
|
||||||
|
url = "2.4.0"
|
||||||
|
|
||||||
[dependencies.tonic-reflection]
|
[dependencies.tonic-reflection]
|
||||||
optional = true
|
optional = true
|
||||||
|
|
85
tvix/build/src/buildservice/from_addr.rs
Normal file
85
tvix/build/src/buildservice/from_addr.rs
Normal file
|
@ -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<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 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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,9 +3,11 @@ use tonic::async_trait;
|
||||||
use crate::proto::{Build, BuildRequest};
|
use crate::proto::{Build, BuildRequest};
|
||||||
|
|
||||||
mod dummy;
|
mod dummy;
|
||||||
|
mod from_addr;
|
||||||
mod grpc;
|
mod grpc;
|
||||||
|
|
||||||
pub use dummy::DummyBuildService;
|
pub use dummy::DummyBuildService;
|
||||||
|
pub use from_addr::from_addr;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait BuildService: Send + Sync {
|
pub trait BuildService: Send + Sync {
|
||||||
|
|
Loading…
Reference in a new issue