feat(tvix/nix-daemon): New operation AddToStoreNar

This operation is particularly used when invoking the following
nix commands:

```
nix-store --add-fixed some-path
nix-store --add-fixed --recursive some-path
```

Change-Id: I0f9b129c838c00e10415881f1e6e0d7bc1d7a3a6
Reviewed-on: https://cl.tvl.fyi/c/depot/+/12800
Tested-by: BuildkiteCI
Reviewed-by: flokli <flokli@flokli.de>
This commit is contained in:
Vova Kryachko 2024-11-24 13:33:04 -05:00 committed by Vladimir Kryachko
parent 8ef9ba82a8
commit e9acde3c42
5 changed files with 289 additions and 19 deletions

View file

@ -60,7 +60,7 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
}
async fn run(cli: Cli) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let (_blob_service, _directory_service, path_info_service, _nar_calculation_service) =
let (blob_service, directory_service, path_info_service, _nar_calculation_service) =
construct_services(cli.service_addrs).await?;
let listen_address = cli.listen_args.listen_address.unwrap_or_else(|| {
@ -76,7 +76,11 @@ async fn run(cli: Cli) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
)
.await?;
let io = Arc::new(TvixDaemon::new(path_info_service));
let io = Arc::new(TvixDaemon::new(
blob_service,
directory_service,
path_info_service,
));
while let Ok((connection, _)) = listener.accept().await {
let io = io.clone();

View file

@ -4,20 +4,35 @@ use std::{
};
use nix_compat::{
nix_daemon::{types::UnkeyedValidPathInfo, NixDaemonIO},
nix_daemon::{
types::{AddToStoreNarRequest, UnkeyedValidPathInfo},
NixDaemonIO,
},
nixbase32,
store_path::StorePath,
store_path::{build_ca_path, StorePath},
};
use tvix_store::{path_info::PathInfo, pathinfoservice::PathInfoService};
use tracing::warn;
use tvix_castore::{blobservice::BlobService, directoryservice::DirectoryService};
use tvix_store::{nar::ingest_nar_and_hash, path_info::PathInfo, pathinfoservice::PathInfoService};
#[allow(dead_code)]
pub struct TvixDaemon {
blob_service: Arc<dyn BlobService>,
directory_service: Arc<dyn DirectoryService>,
path_info_service: Arc<dyn PathInfoService>,
}
impl TvixDaemon {
pub fn new(path_info_service: Arc<dyn PathInfoService>) -> Self {
Self { path_info_service }
pub fn new(
blob_service: Arc<dyn BlobService>,
directory_service: Arc<dyn DirectoryService>,
path_info_service: Arc<dyn PathInfoService>,
) -> Self {
Self {
blob_service,
directory_service,
path_info_service,
}
}
}
@ -48,6 +63,60 @@ impl NixDaemonIO for TvixDaemon {
None => Ok(None),
}
}
async fn add_to_store_nar<R>(&self, request: AddToStoreNarRequest, reader: &mut R) -> Result<()>
where
R: tokio::io::AsyncRead + Send + Unpin,
{
let (root_node, nar_sha256, nar_size) = ingest_nar_and_hash(
self.blob_service.clone(),
self.directory_service.clone(),
reader,
&request.ca,
)
.await
.map_err(|e| Error::other(e.to_string()))?;
if nar_size != request.nar_size || nar_sha256 != *request.nar_hash {
warn!(
nar_hash.expected = nixbase32::encode(&*request.nar_hash),
nar_hash.actual = nixbase32::encode(&nar_sha256),
"nar hash mismatch"
);
return Err(Error::other(
"ingested nar ended up different from what was specified in the request",
));
}
if let Some(cahash) = &request.ca {
let actual_path: StorePath<String> = build_ca_path(
request.path.name(),
cahash,
request.references.iter().map(|p| p.to_absolute_path()),
false,
)
.map_err(Error::other)?;
if actual_path != request.path {
return Err(Error::other("path mismatch"));
}
}
let path_info = PathInfo {
store_path: request.path,
node: root_node,
references: request.references,
nar_size,
nar_sha256,
signatures: request.signatures,
deriver: request.deriver,
ca: request.ca,
};
self.path_info_service
.put(path_info)
.await
.map_err(|e| Error::other(e.to_string()))?;
Ok(())
}
}
// PathInfo lives in the tvix-store crate, but does not depend on nix-compat's wire feature,