1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
use prost::Message;
use std::path::PathBuf;
use crate::proto::get_path_info_request::ByWhat;
use crate::proto::path_info_service_server::PathInfoService;
use crate::proto::CalculateNarResponse;
use crate::proto::GetPathInfoRequest;
use crate::proto::Node;
use crate::proto::PathInfo;
use nix_compat::store_path::DIGEST_SIZE;
use tonic::{Request, Response, Result, Status};
use tracing::{instrument, warn};
const NOT_IMPLEMENTED_MSG: &str = "not implemented";
/// SledPathInfoService stores PathInfo in a [sled](https://github.com/spacejam/sled).
///
/// The PathInfo messages are stored as encoded protos, and keyed by their output hash,
/// as that's currently the only request type available.
pub struct SledPathInfoService {
db: sled::Db,
}
impl SledPathInfoService {
pub fn new(p: PathBuf) -> Result<Self, anyhow::Error> {
let config = sled::Config::default().use_compression(true).path(p);
let db = config.open()?;
Ok(Self { db })
}
}
#[tonic::async_trait]
impl PathInfoService for SledPathInfoService {
#[instrument(skip(self))]
async fn get(&self, request: Request<GetPathInfoRequest>) -> Result<Response<PathInfo>> {
match request.into_inner().by_what {
None => Err(Status::unimplemented("by_what needs to be specified")),
Some(ByWhat::ByOutputHash(digest)) => {
if digest.len() != DIGEST_SIZE {
return Err(Status::invalid_argument("invalid digest length"));
}
match self.db.get(digest) {
Ok(None) => Err(Status::not_found("PathInfo not found")),
Ok(Some(data)) => match PathInfo::decode(&*data) {
Ok(path_info) => Ok(Response::new(path_info)),
Err(e) => {
warn!("failed to decode stored PathInfo: {}", e);
Err(Status::internal("failed to decode stored PathInfo"))
}
},
Err(e) => {
warn!("failed to retrieve PathInfo: {}", e);
Err(Status::internal("error during PathInfo lookup"))
}
}
}
}
}
#[instrument(skip(self))]
async fn put(&self, request: Request<PathInfo>) -> Result<Response<PathInfo>> {
let path_info = request.into_inner();
// Call validate on the received PathInfo message.
match path_info.validate() {
Err(e) => Err(Status::invalid_argument(e.to_string())),
// In case the PathInfo is valid, and we were able to extract a NixPath, store it in the database.
// This overwrites existing PathInfo objects.
Ok(nix_path) => match self.db.insert(nix_path.digest, path_info.encode_to_vec()) {
Ok(_) => Ok(Response::new(path_info)),
Err(e) => {
warn!("failed to insert PathInfo: {}", e);
Err(Status::internal("failed to insert PathInfo"))
}
},
}
}
#[instrument(skip(self))]
async fn calculate_nar(
&self,
_request: Request<Node>,
) -> Result<Response<CalculateNarResponse>> {
warn!(NOT_IMPLEMENTED_MSG);
Err(Status::unimplemented(NOT_IMPLEMENTED_MSG))
}
}
|