feat(tvix/derivation): implement Derivation::validate()
Change-Id: I87dfadda872439e108e5f678a5da63dd5b1915d1 Reviewed-on: https://cl.tvl.fyi/c/depot/+/7732 Reviewed-by: tazjin <tazjin@tvl.su> Tested-by: BuildkiteCI
This commit is contained in:
		
							parent
							
								
									407a9cd90f
								
							
						
					
					
						commit
						cc626d686c
					
				
					 7 changed files with 207 additions and 2 deletions
				
			
		
							
								
								
									
										1
									
								
								tvix/Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								tvix/Cargo.lock
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -492,6 +492,7 @@ checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
 | 
			
		|||
name = "derivation"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "anyhow",
 | 
			
		||||
 "glob",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1155,6 +1155,33 @@ rec {
 | 
			
		|||
          "rustc-hash" = [ "dep:rustc-hash" ];
 | 
			
		||||
        };
 | 
			
		||||
      };
 | 
			
		||||
      "cpufeatures" = rec {
 | 
			
		||||
        crateName = "cpufeatures";
 | 
			
		||||
        version = "0.2.5";
 | 
			
		||||
        edition = "2018";
 | 
			
		||||
        sha256 = "08535izlz4kx8z1kkcp0gy80gqk7k19dqiiysj6r5994bsyrgn98";
 | 
			
		||||
        authors = [
 | 
			
		||||
          "RustCrypto Developers"
 | 
			
		||||
        ];
 | 
			
		||||
        dependencies = [
 | 
			
		||||
          {
 | 
			
		||||
            name = "libc";
 | 
			
		||||
            packageId = "libc";
 | 
			
		||||
            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-apple-darwin");
 | 
			
		||||
          }
 | 
			
		||||
          {
 | 
			
		||||
            name = "libc";
 | 
			
		||||
            packageId = "libc";
 | 
			
		||||
            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-linux-android");
 | 
			
		||||
          }
 | 
			
		||||
          {
 | 
			
		||||
            name = "libc";
 | 
			
		||||
            packageId = "libc";
 | 
			
		||||
            target = { target, features }: (("aarch64" == target."arch") && ("linux" == target."os"));
 | 
			
		||||
          }
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
      };
 | 
			
		||||
      "criterion" = rec {
 | 
			
		||||
        crateName = "criterion";
 | 
			
		||||
        version = "0.4.0";
 | 
			
		||||
| 
						 | 
				
			
			@ -1473,6 +1500,10 @@ rec {
 | 
			
		|||
          then lib.cleanSourceWith { filter = sourceFilter; src = ./derivation; }
 | 
			
		||||
          else ./derivation;
 | 
			
		||||
        dependencies = [
 | 
			
		||||
          {
 | 
			
		||||
            name = "anyhow";
 | 
			
		||||
            packageId = "anyhow";
 | 
			
		||||
          }
 | 
			
		||||
          {
 | 
			
		||||
            name = "glob";
 | 
			
		||||
            packageId = "glob";
 | 
			
		||||
| 
						 | 
				
			
			@ -1482,12 +1513,24 @@ rec {
 | 
			
		|||
            packageId = "serde";
 | 
			
		||||
            features = [ "derive" ];
 | 
			
		||||
          }
 | 
			
		||||
          {
 | 
			
		||||
            name = "sha2";
 | 
			
		||||
            packageId = "sha2";
 | 
			
		||||
          }
 | 
			
		||||
          {
 | 
			
		||||
            name = "tvix-store";
 | 
			
		||||
            packageId = "tvix-store";
 | 
			
		||||
          }
 | 
			
		||||
        ];
 | 
			
		||||
        devDependencies = [
 | 
			
		||||
          {
 | 
			
		||||
            name = "serde_json";
 | 
			
		||||
            packageId = "serde_json";
 | 
			
		||||
          }
 | 
			
		||||
          {
 | 
			
		||||
            name = "test-case";
 | 
			
		||||
            packageId = "test-case";
 | 
			
		||||
          }
 | 
			
		||||
          {
 | 
			
		||||
            name = "test-generator";
 | 
			
		||||
            packageId = "test-generator";
 | 
			
		||||
| 
						 | 
				
			
			@ -4775,6 +4818,46 @@ rec {
 | 
			
		|||
        };
 | 
			
		||||
        resolvedDefaultFeatures = [ "default" "std" ];
 | 
			
		||||
      };
 | 
			
		||||
      "sha2" = rec {
 | 
			
		||||
        crateName = "sha2";
 | 
			
		||||
        version = "0.10.6";
 | 
			
		||||
        edition = "2018";
 | 
			
		||||
        sha256 = "1h5xrrv2y06kr1gsz4pwrm3lsp206nm2gjxgbf21wfrfzsavgrl2";
 | 
			
		||||
        authors = [
 | 
			
		||||
          "RustCrypto Developers"
 | 
			
		||||
        ];
 | 
			
		||||
        dependencies = [
 | 
			
		||||
          {
 | 
			
		||||
            name = "cfg-if";
 | 
			
		||||
            packageId = "cfg-if";
 | 
			
		||||
          }
 | 
			
		||||
          {
 | 
			
		||||
            name = "cpufeatures";
 | 
			
		||||
            packageId = "cpufeatures";
 | 
			
		||||
            target = { target, features }: (("aarch64" == target."arch") || ("x86_64" == target."arch") || ("x86" == target."arch"));
 | 
			
		||||
          }
 | 
			
		||||
          {
 | 
			
		||||
            name = "digest";
 | 
			
		||||
            packageId = "digest";
 | 
			
		||||
          }
 | 
			
		||||
        ];
 | 
			
		||||
        devDependencies = [
 | 
			
		||||
          {
 | 
			
		||||
            name = "digest";
 | 
			
		||||
            packageId = "digest";
 | 
			
		||||
            features = [ "dev" ];
 | 
			
		||||
          }
 | 
			
		||||
        ];
 | 
			
		||||
        features = {
 | 
			
		||||
          "asm" = [ "sha2-asm" ];
 | 
			
		||||
          "asm-aarch64" = [ "asm" ];
 | 
			
		||||
          "default" = [ "std" ];
 | 
			
		||||
          "oid" = [ "digest/oid" ];
 | 
			
		||||
          "sha2-asm" = [ "dep:sha2-asm" ];
 | 
			
		||||
          "std" = [ "digest/std" ];
 | 
			
		||||
        };
 | 
			
		||||
        resolvedDefaultFeatures = [ "default" "std" ];
 | 
			
		||||
      };
 | 
			
		||||
      "sharded-slab" = rec {
 | 
			
		||||
        crateName = "sharded-slab";
 | 
			
		||||
        version = "0.1.4";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,7 @@ edition = "2021"
 | 
			
		|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
anyhow = "1.0.68"
 | 
			
		||||
glob = "0.3.0"
 | 
			
		||||
serde = { version = "1.0", features = ["derive"] }
 | 
			
		||||
sha2 = "0.10.6"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,9 @@
 | 
			
		|||
mod derivation;
 | 
			
		||||
mod nix_hash;
 | 
			
		||||
mod output;
 | 
			
		||||
mod string_escape;
 | 
			
		||||
mod validate;
 | 
			
		||||
mod write;
 | 
			
		||||
 | 
			
		||||
mod derivation;
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use tvix_store::nixpath::NixPath;
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize)]
 | 
			
		||||
pub struct Output {
 | 
			
		||||
| 
						 | 
				
			
			@ -20,4 +21,9 @@ impl Output {
 | 
			
		|||
    pub fn is_fixed(&self) -> bool {
 | 
			
		||||
        self.hash.is_some()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn validate(&self) -> anyhow::Result<()> {
 | 
			
		||||
        NixPath::from_absolute_path(&self.path)?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,6 +30,16 @@ fn check_serizaliation(path_to_drv_file: &str) {
 | 
			
		|||
    assert_eq!(expected, serialized_derivation);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test_resources("src/tests/derivation_tests/*.drv")]
 | 
			
		||||
fn validate(path_to_drv_file: &str) {
 | 
			
		||||
    let data = read_file(&format!("{}.json", path_to_drv_file));
 | 
			
		||||
    let derivation: Derivation = serde_json::from_str(&data).expect("JSON was not well-formatted");
 | 
			
		||||
 | 
			
		||||
    derivation
 | 
			
		||||
        .validate()
 | 
			
		||||
        .expect("derivation failed to validate")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test_resources("src/tests/derivation_tests/*.drv")]
 | 
			
		||||
fn check_to_string(path_to_drv_file: &str) {
 | 
			
		||||
    let data = read_file(&format!("{}.json", path_to_drv_file));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										104
									
								
								tvix/derivation/src/validate.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								tvix/derivation/src/validate.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,104 @@
 | 
			
		|||
use crate::{derivation::Derivation, write::DOT_FILE_EXT};
 | 
			
		||||
use anyhow::bail;
 | 
			
		||||
use tvix_store::nixpath::NixPath;
 | 
			
		||||
 | 
			
		||||
impl Derivation {
 | 
			
		||||
    /// validate ensures a Derivation struct is properly populated,
 | 
			
		||||
    /// and returns an error if not.
 | 
			
		||||
    /// TODO(flokli): make this proper errors
 | 
			
		||||
    pub fn validate(&self) -> anyhow::Result<()> {
 | 
			
		||||
        // Ensure the number of outputs is > 1
 | 
			
		||||
        if self.outputs.is_empty() {
 | 
			
		||||
            bail!("0 outputs");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Validate all outputs
 | 
			
		||||
        for (output_name, output) in &self.outputs {
 | 
			
		||||
            if output_name.is_empty() {
 | 
			
		||||
                bail!("output_name from outputs may not be empty")
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if output.is_fixed() {
 | 
			
		||||
                if self.outputs.len() != 1 {
 | 
			
		||||
                    bail!("encountered fixed-output, but there's more than 1 output in total");
 | 
			
		||||
                }
 | 
			
		||||
                if output_name != "out" {
 | 
			
		||||
                    bail!("the fixed-output output name must be called 'out'");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            output.validate()?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Validate all input_derivations
 | 
			
		||||
        for (input_derivation_path, output_names) in &self.input_derivations {
 | 
			
		||||
            // Validate input_derivation_path
 | 
			
		||||
            NixPath::from_absolute_path(input_derivation_path)?;
 | 
			
		||||
            if !input_derivation_path.ends_with(DOT_FILE_EXT) {
 | 
			
		||||
                bail!(
 | 
			
		||||
                    "derivation {} does not end with .drv",
 | 
			
		||||
                    input_derivation_path
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if output_names.is_empty() {
 | 
			
		||||
                bail!(
 | 
			
		||||
                    "output_names list for {} may not be empty",
 | 
			
		||||
                    input_derivation_path
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for (i, output_name) in output_names.iter().enumerate() {
 | 
			
		||||
                if output_name.is_empty() {
 | 
			
		||||
                    bail!(
 | 
			
		||||
                        "output name entry for {} may not be empty",
 | 
			
		||||
                        input_derivation_path
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                // if i is at least 1, peek at the previous element to ensure output_names are sorted.
 | 
			
		||||
                if i > 0 && (output_names[i - 1] >= *output_name) {
 | 
			
		||||
                    bail!(
 | 
			
		||||
                        "invalid input derivation output order: {} < {}",
 | 
			
		||||
                        output_name,
 | 
			
		||||
                        output_names[i - 1],
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Validate all input_sources
 | 
			
		||||
        for (i, input_source) in self.input_sources.iter().enumerate() {
 | 
			
		||||
            NixPath::from_absolute_path(input_source)?;
 | 
			
		||||
 | 
			
		||||
            if i > 0 && self.input_sources[i - 1] >= *input_source {
 | 
			
		||||
                bail!(
 | 
			
		||||
                    "invalid input source order: {} < {}",
 | 
			
		||||
                    input_source,
 | 
			
		||||
                    self.input_sources[i - 1],
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // validate platform
 | 
			
		||||
        if self.system.is_empty() {
 | 
			
		||||
            bail!("required attribute 'platform' missing");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // validate builder
 | 
			
		||||
        if self.builder.is_empty() {
 | 
			
		||||
            bail!("required attribute 'builder' missing");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // validate env, none of the keys may be empty.
 | 
			
		||||
        // We skip the `name` validation seen in go-nix.
 | 
			
		||||
        for k in self.environment.keys() {
 | 
			
		||||
            if k.is_empty() {
 | 
			
		||||
                bail!("found empty environment variable key");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue