forked from DGNum/colmena
Directly serialize Nix expressions as quoted strings
This commit is contained in:
parent
271d9ae576
commit
092e5848ab
3 changed files with 80 additions and 57 deletions
73
src/nix/expression.rs
Normal file
73
src/nix/expression.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
//! Nix expression serializer.
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
/// A Nix expression.
|
||||
pub trait NixExpression: Send + Sync {
|
||||
/// Returns the full Nix expression to be evaluated.
|
||||
fn expression(&self) -> String;
|
||||
|
||||
/// Returns whether this expression requires the use of flakes.
|
||||
fn requires_flakes(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// A serialized Nix expression.
|
||||
pub struct SerializedNixExpression(String);
|
||||
|
||||
impl NixExpression for String {
|
||||
fn expression(&self) -> String {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl SerializedNixExpression {
|
||||
pub fn new<T>(data: T) -> Self
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
let json = serde_json::to_string(&data).expect("Could not serialize data");
|
||||
let quoted = nix_quote(&json);
|
||||
|
||||
Self(quoted)
|
||||
}
|
||||
}
|
||||
|
||||
impl NixExpression for SerializedNixExpression {
|
||||
fn expression(&self) -> String {
|
||||
format!("(builtins.fromJSON {})", &self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Turns a string into a quoted Nix string expression.
|
||||
fn nix_quote(s: &str) -> String {
|
||||
let inner = s
|
||||
.replace('\\', r#"\\"#)
|
||||
.replace('"', r#"\""#)
|
||||
.replace("${", r#"\${"#);
|
||||
|
||||
format!("\"{}\"", inner)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_nix_quote() {
|
||||
let cases = [
|
||||
(r#"["a", "b"]"#, r#""[\"a\", \"b\"]""#),
|
||||
(
|
||||
r#"["\"a\"", "\"b\""]"#,
|
||||
r#""[\"\\\"a\\\"\", \"\\\"b\\\"\"]""#,
|
||||
),
|
||||
(r#"${dontExpandMe}"#, r#""\${dontExpandMe}""#),
|
||||
(r#"\${dontExpandMe}"#, r#""\\\${dontExpandMe}""#),
|
||||
];
|
||||
|
||||
for (orig, quoted) in cases {
|
||||
assert_eq!(quoted, nix_quote(orig));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,11 +5,8 @@ mod tests;
|
|||
|
||||
use std::collections::HashMap;
|
||||
use std::convert::AsRef;
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use serde::Serialize;
|
||||
use tempfile::{NamedTempFile, TempPath};
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::OnceCell;
|
||||
use validator::Validate;
|
||||
|
@ -17,7 +14,7 @@ use validator::Validate;
|
|||
use super::deployment::TargetNode;
|
||||
use super::{
|
||||
Flake, MetaConfig, NixExpression, NixOptions, NodeConfig, NodeFilter, NodeName,
|
||||
ProfileDerivation, StorePath,
|
||||
ProfileDerivation, SerializedNixExpression, StorePath,
|
||||
};
|
||||
use crate::error::ColmenaResult;
|
||||
use crate::job::JobHandle;
|
||||
|
@ -60,15 +57,6 @@ struct NixInstantiate<'hive> {
|
|||
expression: String,
|
||||
}
|
||||
|
||||
/// A serialized Nix expression.
|
||||
///
|
||||
/// Very hacky so should be avoided as much as possible. But I suppose it's
|
||||
/// more robust than attempting to generate Nix expressions directly or
|
||||
/// escaping a JSON string to strip off Nix interpolation.
|
||||
struct SerializedNixExpression {
|
||||
json_file: TempPath,
|
||||
}
|
||||
|
||||
/// An expression to evaluate the system profiles of selected nodes.
|
||||
struct EvalSelectedExpression<'hive> {
|
||||
hive: &'hive Hive,
|
||||
|
@ -290,7 +278,7 @@ impl Hive {
|
|||
&self,
|
||||
nodes: &[NodeName],
|
||||
) -> ColmenaResult<HashMap<NodeName, NodeConfig>> {
|
||||
let nodes_expr = SerializedNixExpression::new(nodes)?;
|
||||
let nodes_expr = SerializedNixExpression::new(nodes);
|
||||
|
||||
let configs: HashMap<NodeName, NodeConfig> = self
|
||||
.nix_instantiate(&format!(
|
||||
|
@ -322,7 +310,7 @@ impl Hive {
|
|||
nodes: &[NodeName],
|
||||
job: Option<JobHandle>,
|
||||
) -> ColmenaResult<HashMap<NodeName, ProfileDerivation>> {
|
||||
let nodes_expr = SerializedNixExpression::new(nodes)?;
|
||||
let nodes_expr = SerializedNixExpression::new(nodes);
|
||||
|
||||
let expr = format!("hive.evalSelectedDrvPaths {}", nodes_expr.expression());
|
||||
|
||||
|
@ -344,7 +332,7 @@ impl Hive {
|
|||
|
||||
/// Returns the expression to evaluate selected nodes.
|
||||
pub fn eval_selected_expr(&self, nodes: &[NodeName]) -> ColmenaResult<impl NixExpression + '_> {
|
||||
let nodes_expr = SerializedNixExpression::new(nodes)?;
|
||||
let nodes_expr = SerializedNixExpression::new(nodes);
|
||||
|
||||
Ok(EvalSelectedExpression {
|
||||
hive: self,
|
||||
|
@ -446,30 +434,6 @@ impl<'hive> NixInstantiate<'hive> {
|
|||
}
|
||||
}
|
||||
|
||||
impl SerializedNixExpression {
|
||||
pub fn new<T>(data: T) -> ColmenaResult<Self>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
let mut tmp = NamedTempFile::new()?;
|
||||
let json = serde_json::to_vec(&data).expect("Could not serialize data");
|
||||
tmp.write_all(&json)?;
|
||||
|
||||
Ok(Self {
|
||||
json_file: tmp.into_temp_path(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl NixExpression for SerializedNixExpression {
|
||||
fn expression(&self) -> String {
|
||||
format!(
|
||||
"(builtins.fromJSON (builtins.readFile {}))",
|
||||
self.json_file.to_str().unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'hive> NixExpression for EvalSelectedExpression<'hive> {
|
||||
fn expression(&self) -> String {
|
||||
format!(
|
||||
|
|
|
@ -39,6 +39,9 @@ pub use node_filter::NodeFilter;
|
|||
|
||||
pub mod evaluator;
|
||||
|
||||
pub mod expression;
|
||||
pub use expression::{NixExpression, SerializedNixExpression};
|
||||
|
||||
/// Path to the main system profile.
|
||||
pub const SYSTEM_PROFILE: &str = "/nix/var/nix/profiles/system";
|
||||
|
||||
|
@ -104,17 +107,6 @@ pub struct NixOptions {
|
|||
builders: Option<String>,
|
||||
}
|
||||
|
||||
/// A Nix expression.
|
||||
pub trait NixExpression: Send + Sync {
|
||||
/// Returns the full Nix expression to be evaluated.
|
||||
fn expression(&self) -> String;
|
||||
|
||||
/// Returns whether this expression requires the use of flakes.
|
||||
fn requires_flakes(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeName {
|
||||
/// Returns the string.
|
||||
pub fn as_str(&self) -> &str {
|
||||
|
@ -218,12 +210,6 @@ impl NixOptions {
|
|||
}
|
||||
}
|
||||
|
||||
impl NixExpression for String {
|
||||
fn expression(&self) -> String {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_keys(keys: &HashMap<String, Key>) -> Result<(), ValidationErrorType> {
|
||||
// Bad secret names:
|
||||
// - /etc/passwd
|
||||
|
|
Loading…
Reference in a new issue