record whether an agent's config sets privateUsers in its closure record
This commit is contained in:
parent
7412a0f1bd
commit
3ca660e558
9 changed files with 178 additions and 6 deletions
|
|
@ -317,6 +317,7 @@ mod tests {
|
||||||
system: "x86_64-linux".to_owned(),
|
system: "x86_64-linux".to_owned(),
|
||||||
built_unix: 1,
|
built_unix: 1,
|
||||||
cached: true,
|
cached: true,
|
||||||
|
private_users: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -285,6 +285,7 @@ fn closure(meta_rev: &str, cached: bool) -> AgentClosure {
|
||||||
system: "x86_64-linux".to_owned(),
|
system: "x86_64-linux".to_owned(),
|
||||||
built_unix: 1,
|
built_unix: 1,
|
||||||
cached,
|
cached,
|
||||||
|
private_users: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -422,12 +422,14 @@ async fn handle_meta(
|
||||||
out_path,
|
out_path,
|
||||||
system,
|
system,
|
||||||
cached,
|
cached,
|
||||||
|
private_users,
|
||||||
} => tracing::info!(
|
} => tracing::info!(
|
||||||
agent = %job.agent,
|
agent = %job.agent,
|
||||||
meta_rev = %job.meta_rev,
|
meta_rev = %job.meta_rev,
|
||||||
%out_path,
|
%out_path,
|
||||||
%system,
|
%system,
|
||||||
cached,
|
cached,
|
||||||
|
private_users,
|
||||||
"agent meta build built"
|
"agent meta build built"
|
||||||
),
|
),
|
||||||
FromMetaReply::Failed { error_tail } => tracing::warn!(
|
FromMetaReply::Failed { error_tail } => tracing::warn!(
|
||||||
|
|
@ -641,6 +643,7 @@ mod tests {
|
||||||
out_path: "/nix/store/x".to_owned(),
|
out_path: "/nix/store/x".to_owned(),
|
||||||
system: "s".to_owned(),
|
system: "s".to_owned(),
|
||||||
cached: false,
|
cached: false,
|
||||||
|
private_users: false,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
@ -656,6 +659,7 @@ mod tests {
|
||||||
out_path: "/nix/store/x".to_owned(),
|
out_path: "/nix/store/x".to_owned(),
|
||||||
system: "s".to_owned(),
|
system: "s".to_owned(),
|
||||||
cached: false,
|
cached: false,
|
||||||
|
private_users: false,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,7 @@ impl Builder {
|
||||||
out_path,
|
out_path,
|
||||||
system: self.system.clone(),
|
system: self.system.clone(),
|
||||||
cached: false,
|
cached: false,
|
||||||
|
private_users: self.private_users(job, token).await,
|
||||||
},
|
},
|
||||||
Reply::Failed { error_tail } => FromMetaReply::Failed { error_tail },
|
Reply::Failed { error_tail } => FromMetaReply::Failed { error_tail },
|
||||||
},
|
},
|
||||||
|
|
@ -170,12 +171,39 @@ impl Builder {
|
||||||
/// the hives build. No input is overridden: the committed `flake.lock`
|
/// the hives build. No input is overridden: the committed `flake.lock`
|
||||||
/// decides all of them.
|
/// decides all of them.
|
||||||
fn meta_attr(&self, job: &BuildFromMeta) -> String {
|
fn meta_attr(&self, job: &BuildFromMeta) -> String {
|
||||||
|
format!("{}.system.build.toplevel", self.meta_config(job))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The agent's evaluated NixOS config in `internal/meta` at the commit.
|
||||||
|
fn meta_config(&self, job: &BuildFromMeta) -> String {
|
||||||
format!(
|
format!(
|
||||||
"git+{}/{META_REPO_PATH}.git?rev={}#nixosConfigurations.{}.config.system.build.toplevel",
|
"git+{}/{META_REPO_PATH}.git?rev={}#nixosConfigurations.{}.config",
|
||||||
self.forge_url, job.meta_rev, job.agent
|
self.forge_url, job.meta_rev, job.agent
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the agent's config sets `services.hyperhive.agent.user.privateUsers`,
|
||||||
|
/// read by `nix eval` of the flake the build just evaluated, in this
|
||||||
|
/// sandbox. Anything but a clean `true` is false: an agent whose module
|
||||||
|
/// predates the option, a failed eval and a timeout all keep the agent out
|
||||||
|
/// of a user namespace.
|
||||||
|
async fn private_users(&self, job: &BuildFromMeta, token: &str) -> bool {
|
||||||
|
let mut cmd = self.nix_command(token);
|
||||||
|
cmd.args(EVAL_FLAGS)
|
||||||
|
.arg(format!("{}.{PRIVATE_USERS_OPTION}", self.meta_config(job)));
|
||||||
|
match run_raw(cmd, self.deadline).await {
|
||||||
|
Ok((status, out, _)) if status.success() => parse_flag(&out),
|
||||||
|
Ok((status, _, err)) => {
|
||||||
|
tracing::info!(agent = %job.agent, %status, error = %tail(&String::from_utf8_lossy(&err), status), "privateUsers not read; the agent stays out of a user namespace");
|
||||||
|
false
|
||||||
|
}
|
||||||
|
Err(why) => {
|
||||||
|
tracing::warn!(agent = %job.agent, %why, "privateUsers not read; the agent stays out of a user namespace");
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The config commit as a flake reference, fetched through the PR's head
|
/// The config commit as a flake reference, fetched through the PR's head
|
||||||
/// ref.
|
/// ref.
|
||||||
fn agent_url(&self, job: &BuildAgent) -> String {
|
fn agent_url(&self, job: &BuildAgent) -> String {
|
||||||
|
|
@ -199,6 +227,26 @@ const BUILD_FLAGS: [&str; 8] = [
|
||||||
"--print-build-logs",
|
"--print-build-logs",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/// What every `nix eval` of an option starts with: JSON out, nothing written to
|
||||||
|
/// the flake's lock, the flake's own `nix.conf` settings refused.
|
||||||
|
const EVAL_FLAGS: [&str; 7] = [
|
||||||
|
"eval",
|
||||||
|
"--json",
|
||||||
|
"--no-write-lock-file",
|
||||||
|
"--option",
|
||||||
|
"accept-flake-config",
|
||||||
|
"false",
|
||||||
|
"--quiet",
|
||||||
|
];
|
||||||
|
|
||||||
|
/// The agent option the controller records, relative to the evaluated config.
|
||||||
|
const PRIVATE_USERS_OPTION: &str = "services.hyperhive.agent.user.privateUsers";
|
||||||
|
|
||||||
|
/// `nix eval --json` of a bool: true only for exactly `true`.
|
||||||
|
fn parse_flag(out: &[u8]) -> bool {
|
||||||
|
out.trim_ascii() == b"true"
|
||||||
|
}
|
||||||
|
|
||||||
/// `nix flake lock` of the flake in the working directory. The flake's config
|
/// `nix flake lock` of the flake in the working directory. The flake's config
|
||||||
/// is not trusted, and nothing is built.
|
/// is not trusted, and nothing is built.
|
||||||
const LOCK_ARGS: &[&str] = &["flake", "lock", "--option", "accept-flake-config", "false"];
|
const LOCK_ARGS: &[&str] = &["flake", "lock", "--option", "accept-flake-config", "false"];
|
||||||
|
|
@ -484,6 +532,47 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The reply carries what the evaluated config says about the option, and
|
||||||
|
/// anything but a clean `true` reads as false. The `true` arm is the
|
||||||
|
/// control for the others.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn a_meta_build_reports_the_agents_private_users_option() {
|
||||||
|
for (i, (answer, want)) in [
|
||||||
|
("echo true", true),
|
||||||
|
("echo false", false),
|
||||||
|
("echo null", false),
|
||||||
|
("echo true; exit 1", false),
|
||||||
|
("echo error: attribute missing >&2; exit 1", false),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
let nix = fake_nix(
|
||||||
|
&format!("private-users-{i}"),
|
||||||
|
&format!("if [ \"$1\" = eval ]; then {answer}; else echo /nix/store/x; fi\n"),
|
||||||
|
);
|
||||||
|
let b = Builder { nix, ..builder() };
|
||||||
|
let reply = b.build_from_meta(&meta_job(), "t").await;
|
||||||
|
assert!(
|
||||||
|
matches!(&reply, FromMetaReply::Built { private_users, .. } if *private_users == want),
|
||||||
|
"{answer}: {reply:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn the_option_is_read_from_the_evaluated_config_not_the_toplevel() {
|
||||||
|
assert_eq!(
|
||||||
|
format!(
|
||||||
|
"{}.{PRIVATE_USERS_OPTION}",
|
||||||
|
builder().meta_config(&meta_job())
|
||||||
|
),
|
||||||
|
"git+https://forge.example/internal/meta.git\
|
||||||
|
?rev=0123456789abcdef0123456789abcdef01234567\
|
||||||
|
#nixosConfigurations.atlas.config.services.hyperhive.agent.user.privateUsers"
|
||||||
|
);
|
||||||
|
assert!(parse_flag(b"true\n") && !parse_flag(b"false\n") && !parse_flag(b""));
|
||||||
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn a_meta_job_that_would_bend_the_reference_is_refused() {
|
fn a_meta_job_that_would_bend_the_reference_is_refused() {
|
||||||
for bad in [
|
for bad in [
|
||||||
|
|
@ -520,6 +609,7 @@ mod tests {
|
||||||
out_path: format!("{} {}", BUILD_FLAGS.join(" "), b.meta_attr(&meta_job())),
|
out_path: format!("{} {}", BUILD_FLAGS.join(" "), b.meta_attr(&meta_job())),
|
||||||
system: "x86_64-linux".to_owned(),
|
system: "x86_64-linux".to_owned(),
|
||||||
cached: false,
|
cached: false,
|
||||||
|
private_users: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -208,7 +208,10 @@ pub async fn cache_built(
|
||||||
let (
|
let (
|
||||||
Some(cache),
|
Some(cache),
|
||||||
FromMetaReply::Built {
|
FromMetaReply::Built {
|
||||||
out_path, system, ..
|
out_path,
|
||||||
|
system,
|
||||||
|
private_users,
|
||||||
|
..
|
||||||
},
|
},
|
||||||
) = (cache, &reply)
|
) = (cache, &reply)
|
||||||
else {
|
else {
|
||||||
|
|
@ -234,6 +237,7 @@ pub async fn cache_built(
|
||||||
out_path: out_path.clone(),
|
out_path: out_path.clone(),
|
||||||
system: system.clone(),
|
system: system.clone(),
|
||||||
cached: true,
|
cached: true,
|
||||||
|
private_users: *private_users,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(reason) => {
|
Err(reason) => {
|
||||||
|
|
@ -258,6 +262,7 @@ mod tests {
|
||||||
out_path: OUT.to_owned(),
|
out_path: OUT.to_owned(),
|
||||||
system: "x86_64-linux".to_owned(),
|
system: "x86_64-linux".to_owned(),
|
||||||
cached: false,
|
cached: false,
|
||||||
|
private_users: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -288,12 +288,13 @@ async fn rebuild(world: &impl World, agent: &str, stored: Stored) -> Outcome {
|
||||||
return Outcome::Unavailable;
|
return Outcome::Unavailable;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let (out_path, system, cached) = match reply {
|
let (out_path, system, cached, private_users) = match reply {
|
||||||
FromMetaReply::Built {
|
FromMetaReply::Built {
|
||||||
out_path,
|
out_path,
|
||||||
system,
|
system,
|
||||||
cached,
|
cached,
|
||||||
} => (out_path, system, cached),
|
private_users,
|
||||||
|
} => (out_path, system, cached, private_users),
|
||||||
FromMetaReply::Failed { error_tail } => {
|
FromMetaReply::Failed { error_tail } => {
|
||||||
tracing::warn!(%agent, %meta_rev, %error_tail, "cache miss: the rebuild failed");
|
tracing::warn!(%agent, %meta_rev, %error_tail, "cache miss: the rebuild failed");
|
||||||
return Outcome::Failed;
|
return Outcome::Failed;
|
||||||
|
|
@ -321,6 +322,7 @@ async fn rebuild(world: &impl World, agent: &str, stored: Stored) -> Outcome {
|
||||||
system,
|
system,
|
||||||
built_unix: unix_now(),
|
built_unix: unix_now(),
|
||||||
cached: true,
|
cached: true,
|
||||||
|
private_users,
|
||||||
};
|
};
|
||||||
match replace_if_current(world, agent, &now).await {
|
match replace_if_current(world, agent, &now).await {
|
||||||
Ok(true) => Outcome::PathChanged,
|
Ok(true) => Outcome::PathChanged,
|
||||||
|
|
@ -558,6 +560,7 @@ mod tests {
|
||||||
system: "x86_64-linux".to_owned(),
|
system: "x86_64-linux".to_owned(),
|
||||||
built_unix: 7,
|
built_unix: 7,
|
||||||
cached,
|
cached,
|
||||||
|
private_users: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -644,6 +647,7 @@ mod tests {
|
||||||
out_path: out_path.to_owned(),
|
out_path: out_path.to_owned(),
|
||||||
system: "x86_64-linux".to_owned(),
|
system: "x86_64-linux".to_owned(),
|
||||||
cached,
|
cached,
|
||||||
|
private_users: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -888,6 +892,32 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The record a rebuild at another path writes carries what the builder
|
||||||
|
/// answered about the user namespace, not what the old record held.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn the_replacement_record_carries_the_rebuilds_private_users_answer() {
|
||||||
|
let other = path("iris-other");
|
||||||
|
let FromMetaReply::Built {
|
||||||
|
out_path,
|
||||||
|
system,
|
||||||
|
cached,
|
||||||
|
..
|
||||||
|
} = built(&other, true)
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
let answer = FromMetaReply::Built {
|
||||||
|
out_path,
|
||||||
|
system,
|
||||||
|
cached,
|
||||||
|
private_users: true,
|
||||||
|
};
|
||||||
|
let world = Fake::missing(&["iris"]).answering("iris", answer);
|
||||||
|
pass_at(&world, &mut Backoff::default(), Instant::now()).await;
|
||||||
|
let writes = world.writes();
|
||||||
|
assert_eq!(writes.len(), 1);
|
||||||
|
assert!(writes[0].1.private_users);
|
||||||
|
}
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn another_path_leaves_a_record_that_moved_on() {
|
async fn another_path_leaves_a_record_that_moved_on() {
|
||||||
let world = Fake::missing(&["iris"]).answering("iris", built(&path("iris-other"), true));
|
let world = Fake::missing(&["iris"]).answering("iris", built(&path("iris-other"), true));
|
||||||
|
|
|
||||||
|
|
@ -370,6 +370,7 @@ pub(crate) async fn run(
|
||||||
out_path,
|
out_path,
|
||||||
system,
|
system,
|
||||||
cached,
|
cached,
|
||||||
|
private_users,
|
||||||
}) => {
|
}) => {
|
||||||
let built_rev = meta_rev.clone();
|
let built_rev = meta_rev.clone();
|
||||||
let closure = AgentClosure {
|
let closure = AgentClosure {
|
||||||
|
|
@ -378,6 +379,7 @@ pub(crate) async fn run(
|
||||||
system,
|
system,
|
||||||
built_unix: unix_now(),
|
built_unix: unix_now(),
|
||||||
cached,
|
cached,
|
||||||
|
private_users,
|
||||||
};
|
};
|
||||||
match queue.record(&agent, &closure).await {
|
match queue.record(&agent, &closure).await {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
|
|
@ -486,10 +488,15 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn built_cached(cached: bool) -> Self {
|
fn built_cached(cached: bool) -> Self {
|
||||||
|
Self::built_with(cached, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn built_with(cached: bool, private_users: bool) -> Self {
|
||||||
Self::answering(Ok(FromMetaReply::Built {
|
Self::answering(Ok(FromMetaReply::Built {
|
||||||
out_path: "/nix/store/x-toplevel".to_owned(),
|
out_path: "/nix/store/x-toplevel".to_owned(),
|
||||||
system: "x86_64-linux".to_owned(),
|
system: "x86_64-linux".to_owned(),
|
||||||
cached,
|
cached,
|
||||||
|
private_users,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -617,6 +624,22 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The record says what the builder said about the agent's user namespace,
|
||||||
|
/// both ways; a builder that said nothing leaves it off.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn the_record_carries_the_builders_private_users_answer() {
|
||||||
|
for private_users in [true, false] {
|
||||||
|
let queue = FakeQueue::built_with(true, private_users);
|
||||||
|
let outcome = run_with(&FakeHead::reading(Ok(Some(REV))), &queue).await;
|
||||||
|
assert_eq!(outcome, Outcome::Done);
|
||||||
|
let recorded = queue.recorded.lock().unwrap();
|
||||||
|
let [(_, closure)] = recorded.as_slice() else {
|
||||||
|
panic!("one record: {recorded:?}")
|
||||||
|
};
|
||||||
|
assert_eq!(closure.private_users, private_users);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Any `nix` failure, a timeout included, fails the node at once with what
|
/// Any `nix` failure, a timeout included, fails the node at once with what
|
||||||
/// the builder said: not a no-verdict failure, and nothing is recorded. The
|
/// the builder said: not a no-verdict failure, and nothing is recorded. The
|
||||||
/// green test above is the control that a record is written when the build
|
/// green test above is the control that a record is written when the build
|
||||||
|
|
@ -898,6 +921,7 @@ mod tests {
|
||||||
system: "x86_64-linux".to_owned(),
|
system: "x86_64-linux".to_owned(),
|
||||||
built_unix: 1,
|
built_unix: 1,
|
||||||
cached: false,
|
cached: false,
|
||||||
|
private_users: true,
|
||||||
};
|
};
|
||||||
client.record("atlas", &closure).await.expect("written");
|
client.record("atlas", &closure).await.expect("written");
|
||||||
closure.store_path = "/nix/store/b".to_owned();
|
closure.store_path = "/nix/store/b".to_owned();
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,12 @@ pub struct AgentClosure {
|
||||||
/// written before the field existed reads as false.
|
/// written before the field existed reads as false.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub cached: bool,
|
pub cached: bool,
|
||||||
|
/// Whether the agent's evaluated config sets
|
||||||
|
/// `services.hyperhive.agent.user.privateUsers`: the agent runs in its own
|
||||||
|
/// user namespace. A record written before the field existed reads as
|
||||||
|
/// false, which keeps the agent out of one.
|
||||||
|
#[serde(default)]
|
||||||
|
pub private_users: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -56,6 +62,7 @@ mod tests {
|
||||||
system: "x86_64-linux".to_owned(),
|
system: "x86_64-linux".to_owned(),
|
||||||
built_unix: 7,
|
built_unix: 7,
|
||||||
cached: true,
|
cached: true,
|
||||||
|
private_users: true,
|
||||||
};
|
};
|
||||||
let wire = serde_json::json!({
|
let wire = serde_json::json!({
|
||||||
"meta_rev": "abc",
|
"meta_rev": "abc",
|
||||||
|
|
@ -63,6 +70,7 @@ mod tests {
|
||||||
"system": "x86_64-linux",
|
"system": "x86_64-linux",
|
||||||
"built_unix": 7,
|
"built_unix": 7,
|
||||||
"cached": true,
|
"cached": true,
|
||||||
|
"private_users": true,
|
||||||
});
|
});
|
||||||
assert_eq!(serde_json::to_value(&value).expect("encodes"), wire);
|
assert_eq!(serde_json::to_value(&value).expect("encodes"), wire);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
@ -72,7 +80,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_record_without_the_field_reads_as_not_cached() {
|
fn a_record_without_the_fields_reads_as_not_cached_and_not_namespaced() {
|
||||||
let old = serde_json::json!({
|
let old = serde_json::json!({
|
||||||
"meta_rev": "abc",
|
"meta_rev": "abc",
|
||||||
"store_path": "/nix/store/x",
|
"store_path": "/nix/store/x",
|
||||||
|
|
@ -81,5 +89,6 @@ mod tests {
|
||||||
});
|
});
|
||||||
let record = serde_json::from_value::<AgentClosure>(old).expect("decodes");
|
let record = serde_json::from_value::<AgentClosure>(old).expect("decodes");
|
||||||
assert!(!record.cached);
|
assert!(!record.cached);
|
||||||
|
assert!(!record.private_users);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,11 @@ pub enum FromMetaReply {
|
||||||
system: String,
|
system: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
cached: bool,
|
cached: bool,
|
||||||
|
/// Whether the agent's evaluated config sets
|
||||||
|
/// `services.hyperhive.agent.user.privateUsers`. A builder that
|
||||||
|
/// predates the field answers without it, which reads as false.
|
||||||
|
#[serde(default)]
|
||||||
|
private_users: bool,
|
||||||
},
|
},
|
||||||
/// The build did not succeed: the last lines `nix` printed, or why it
|
/// The build did not succeed: the last lines `nix` printed, or why it
|
||||||
/// stopped (it timed out, or could not start). The controller holds the
|
/// stopped (it timed out, or could not start). The controller holds the
|
||||||
|
|
@ -262,12 +267,14 @@ mod tests {
|
||||||
out_path: "/nix/store/x".to_owned(),
|
out_path: "/nix/store/x".to_owned(),
|
||||||
system: "x86_64-linux".to_owned(),
|
system: "x86_64-linux".to_owned(),
|
||||||
cached: true,
|
cached: true,
|
||||||
|
private_users: true,
|
||||||
},
|
},
|
||||||
json!({
|
json!({
|
||||||
"state": "built",
|
"state": "built",
|
||||||
"out_path": "/nix/store/x",
|
"out_path": "/nix/store/x",
|
||||||
"system": "x86_64-linux",
|
"system": "x86_64-linux",
|
||||||
"cached": true,
|
"cached": true,
|
||||||
|
"private_users": true,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
|
@ -292,7 +299,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_built_reply_from_a_builder_without_the_field_reads_as_not_cached() {
|
fn a_built_reply_from_a_builder_without_the_fields_reads_as_not_cached_and_not_namespaced() {
|
||||||
let old = json!({"state": "built", "out_path": "/nix/store/x", "system": "s"});
|
let old = json!({"state": "built", "out_path": "/nix/store/x", "system": "s"});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
serde_json::from_value::<FromMetaReply>(old).expect("decodes"),
|
serde_json::from_value::<FromMetaReply>(old).expect("decodes"),
|
||||||
|
|
@ -300,6 +307,7 @@ mod tests {
|
||||||
out_path: "/nix/store/x".to_owned(),
|
out_path: "/nix/store/x".to_owned(),
|
||||||
system: "s".to_owned(),
|
system: "s".to_owned(),
|
||||||
cached: false,
|
cached: false,
|
||||||
|
private_users: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue