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:
		
							parent
							
								
									a247b25097
								
							
						
					
					
						commit
						cada007937
					
				
					 3 changed files with 12 additions and 115 deletions
				
			
		|  | @ -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, | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue