refactor(tvix/build): remove proto::BuildRequest::validate

Change-Id: I96fa98946bf6aff5eedcb220e2b6b3d90c204eec
Reviewed-on: https://cl.tvl.fyi/c/depot/+/12633
Reviewed-by: flokli <flokli@flokli.de>
Tested-by: BuildkiteCI
This commit is contained in:
Marijan Petričević 2024-10-16 12:30:06 -05:00
parent a247b25097
commit cada007937
3 changed files with 12 additions and 115 deletions

View file

@ -35,7 +35,7 @@ use tvix_castore::{Node, PathComponent};
///
/// As of now, we're okay to accept this, but it prevents uploading an
/// entirely-non-IFD subgraph of BuildRequests eagerly.
#[derive(Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub struct BuildRequest {
/// The list of all root nodes that should be visible in `inputs_dir` at the
/// time of the build.
@ -94,7 +94,7 @@ pub struct BuildRequest {
pub refscan_needles: Vec<String>,
}
#[derive(Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub struct EnvVar {
/// name of the environment variable. Must not contain =.
pub key: String,
@ -105,7 +105,7 @@ pub struct EnvVar {
/// Constraints can be things like required architecture and minimum amount of memory.
/// The required input paths are *not* represented in here, because it
/// wouldn't be hermetic enough - see the comment around inputs too.
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum BuildConstraints {
/// The system that's needed to execute the build.
/// Must not be empty.
@ -124,7 +124,7 @@ pub enum BuildConstraints {
ProvideBinSh,
}
#[derive(Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub struct AdditionalFile {
pub path: PathBuf,
pub contents: Bytes,

View file

@ -118,90 +118,6 @@ where
data.tuple_windows().all(|(a, b)| a <= b)
}
impl BuildRequest {
/// Ensures the build request is valid.
/// This means, all input nodes need to be valid, paths in lists need to be sorted,
/// and all restrictions around paths themselves (relative, clean, …) need
// to be fulfilled.
pub fn validate(&self) -> Result<(), ValidateBuildRequestError> {
// validate names. Make sure they're sorted
let mut last_name: bytes::Bytes = "".into();
for (i, node) in self.inputs.iter().enumerate() {
// TODO(flokli): store result somewhere
let (name, _node) = node
.clone()
.into_name_and_node()
.map_err(|e| ValidateBuildRequestError::InvalidInputNode(i, e))?;
if name.as_ref() <= last_name.as_ref() {
return Err(ValidateBuildRequestError::InputNodesNotSorted);
} else {
last_name = name.into()
}
}
// validate working_dir
if !is_clean_relative_path(&self.working_dir) {
Err(ValidateBuildRequestError::InvalidWorkingDir)?;
}
// validate scratch paths
for (i, p) in self.scratch_paths.iter().enumerate() {
if !is_clean_relative_path(p) {
Err(ValidateBuildRequestError::InvalidScratchPath(i))?
}
}
if !is_sorted(self.scratch_paths.iter().map(|e| e.as_bytes())) {
Err(ValidateBuildRequestError::ScratchPathsNotSorted)?;
}
// validate inputs_dir
if !is_clean_relative_path(&self.inputs_dir) {
Err(ValidateBuildRequestError::InvalidInputsDir)?;
}
// validate outputs
for (i, p) in self.outputs.iter().enumerate() {
if !is_clean_relative_path(p) {
Err(ValidateBuildRequestError::InvalidOutputPath(i))?
}
}
if !is_sorted(self.outputs.iter().map(|e| e.as_bytes())) {
Err(ValidateBuildRequestError::OutputsNotSorted)?;
}
// validate environment_vars.
for (i, e) in self.environment_vars.iter().enumerate() {
if e.key.is_empty() || e.key.contains('=') {
Err(ValidateBuildRequestError::InvalidEnvVar(i))?
}
}
if !is_sorted(self.environment_vars.iter().map(|e| e.key.as_bytes())) {
Err(ValidateBuildRequestError::EnvVarNotSorted)?;
}
// validate build constraints
if let Some(constraints) = self.constraints.as_ref() {
constraints
.validate()
.map_err(ValidateBuildRequestError::InvalidBuildConstraints)?;
}
// validate additional_files
for (i, additional_file) in self.additional_files.iter().enumerate() {
if !is_clean_relative_path(&additional_file.path) {
Err(ValidateBuildRequestError::InvalidAdditionalFilePath(i))?
}
}
if !is_sorted(self.additional_files.iter().map(|e| e.path.as_bytes())) {
Err(ValidateBuildRequestError::AdditionalFilesNotSorted)?;
}
Ok(())
}
}
impl TryFrom<BuildRequest> for crate::buildservice::BuildRequest {
type Error = ValidateBuildRequestError;
fn try_from(value: BuildRequest) -> Result<Self, Self::Error> {
@ -311,26 +227,6 @@ pub enum ValidateBuildConstraintsError {
AvailableRoPathsNotSorted,
}
impl build_request::BuildConstraints {
pub fn validate(&self) -> Result<(), ValidateBuildConstraintsError> {
// validate system
if self.system.is_empty() {
Err(ValidateBuildConstraintsError::InvalidSystem)?;
}
// validate available_ro_paths
for (i, p) in self.available_ro_paths.iter().enumerate() {
if !is_clean_absolute_path(p) {
Err(ValidateBuildConstraintsError::InvalidAvailableRoPaths(i))?
}
}
if !is_sorted(self.available_ro_paths.iter().map(|e| e.as_bytes())) {
Err(ValidateBuildConstraintsError::AvailableRoPathsNotSorted)?;
}
Ok(())
}
}
impl From<build_request::EnvVar> for crate::buildservice::EnvVar {
fn from(value: build_request::EnvVar) -> Self {
Self {

View file

@ -6,9 +6,10 @@ use std::collections::BTreeMap;
use bytes::Bytes;
use nix_compat::{derivation::Derivation, nixbase32, store_path::StorePath};
use sha2::{Digest, Sha256};
use tvix_build::buildservice::BuildRequest;
use tvix_build::proto::{
self,
build_request::{AdditionalFile, BuildConstraints, EnvVar},
BuildRequest,
};
use tvix_castore::Node;
@ -43,7 +44,7 @@ pub(crate) fn get_refscan_needles(
.chain(derivation.input_derivations.keys())
}
/// Takes a [Derivation] and turns it into a [BuildRequest].
/// Takes a [Derivation] and turns it into a [proto::BuildRequest].
/// It assumes the Derivation has been validated.
/// It needs two lookup functions:
/// - one translating input sources to a castore node
@ -53,7 +54,7 @@ pub(crate) fn get_refscan_needles(
pub(crate) fn derivation_to_build_request(
derivation: &Derivation,
inputs: BTreeMap<bytes::Bytes, Node>,
) -> std::io::Result<BuildRequest> {
) -> std::io::Result<proto::BuildRequest> {
debug_assert!(derivation.validate(true).is_ok(), "drv must validate");
// produce command_args, which is builder and arguments in a Vec.
@ -113,7 +114,7 @@ pub(crate) fn derivation_to_build_request(
provide_bin_sh: true,
});
let build_request = BuildRequest {
let build_request = proto::BuildRequest {
// Importantly, this must match the order of get_refscan_needles, since users may use that
// function to map back from the found needles to a store path
refscan_needles: get_refscan_needles(derivation)
@ -141,10 +142,10 @@ pub(crate) fn derivation_to_build_request(
.collect(),
};
// FUTUREWORK: switch this function to construct the stricter BuildRequest directly.
debug_assert!(
build_request.validate().is_ok(),
"invalid BuildRequest: {}",
build_request.validate().unwrap_err()
BuildRequest::try_from(build_request.clone()).is_ok(),
"Tvix bug: BuildRequest would not be valid"
);
Ok(build_request)