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 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
use crate::derivation::{Derivation, DerivationError};
use crate::store_path;
impl Derivation {
/// validate ensures a Derivation struct is properly populated,
/// and returns a [DerivationError] if not.
///
/// if `validate_output_paths` is set to false, the output paths are
/// excluded from validation.
///
/// This is helpful to validate struct population before invoking
/// [Derivation::calculate_output_paths].
pub fn validate(&self, validate_output_paths: bool) -> Result<(), DerivationError> {
// Ensure the number of outputs is > 1
if self.outputs.is_empty() {
return Err(DerivationError::NoOutputs());
}
// Validate all outputs
for (output_name, output) in &self.outputs {
// empty output names are invalid.
//
// `drv` is an invalid output name too, as this would cause
// a `builtins.derivation` call to return an attrset with a
// `drvPath` key (which already exists) and has a different
// meaning.
//
// Other output names that don't match the name restrictions from
// [StorePathRef] will fail the [StorePathRef::validate_name] check.
if output_name.is_empty()
|| output_name == "drv"
|| store_path::validate_name(output_name.as_bytes()).is_err()
{
return Err(DerivationError::InvalidOutputName(output_name.to_string()));
}
if output.is_fixed() {
if self.outputs.len() != 1 {
return Err(DerivationError::MoreThanOneOutputButFixed());
}
if output_name != "out" {
return Err(DerivationError::InvalidOutputNameForFixed(
output_name.to_string(),
));
}
}
if let Err(e) = output.validate(validate_output_paths) {
return Err(DerivationError::InvalidOutput(output_name.to_string(), e));
}
}
// Validate all input_derivations
for (input_derivation_path, output_names) in &self.input_derivations {
// Validate input_derivation_path
if !input_derivation_path.name().ends_with(".drv") {
return Err(DerivationError::InvalidInputDerivationPrefix(
input_derivation_path.to_string(),
));
}
if output_names.is_empty() {
return Err(DerivationError::EmptyInputDerivationOutputNames(
input_derivation_path.to_string(),
));
}
for output_name in output_names.iter() {
// empty output names are invalid.
//
// `drv` is an invalid output name too, as this would cause
// a `builtins.derivation` call to return an attrset with a
// `drvPath` key (which already exists) and has a different
// meaning.
//
// Other output names that don't match the name restrictions from
// [StorePath] will fail the [StorePathRef::validate_name] check.
if output_name.is_empty()
|| output_name == "drv"
|| store_path::validate_name(output_name.as_bytes()).is_err()
{
return Err(DerivationError::InvalidInputDerivationOutputName(
input_derivation_path.to_string(),
output_name.to_string(),
));
}
}
}
// validate platform
if self.system.is_empty() {
return Err(DerivationError::InvalidPlatform(self.system.to_string()));
}
// validate builder
if self.builder.is_empty() {
return Err(DerivationError::InvalidBuilder(self.builder.to_string()));
}
// 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() {
return Err(DerivationError::InvalidEnvironmentKey(k.to_string()));
}
}
Ok(())
}
}
#[cfg(test)]
mod test {
use std::collections::BTreeMap;
use crate::derivation::{CAHash, Derivation, Output};
/// Regression test: produce a Derivation that's almost valid, except its
/// fixed-output output has the wrong hash specified.
#[test]
fn output_validate() {
let mut outputs = BTreeMap::new();
outputs.insert(
"out".to_string(),
Output {
path: None,
ca_hash: Some(CAHash::Text([0; 32])), // This is disallowed
},
);
let drv = Derivation {
arguments: vec![],
builder: "/bin/sh".to_string(),
outputs,
system: "x86_64-linux".to_string(),
..Default::default()
};
drv.validate(false).expect_err("must fail");
}
}