closure_pullable judges an agent by its own pins, not the whole meta head
This commit is contained in:
parent
35a49aaf42
commit
567cd12300
1 changed files with 158 additions and 11 deletions
|
|
@ -8,7 +8,8 @@
|
|||
//!
|
||||
//! The verdict is the first of these that fails, in this order:
|
||||
//! no credentials (`no-credentials`), no record (`no-record`), a record built
|
||||
//! from another `internal/meta` commit than the head (`rev-mismatch`), a record
|
||||
//! from another `internal/meta` commit than the head that pins the agent
|
||||
//! differently (`rev-mismatch`), a record
|
||||
//! the builder did not push (`not-cached`), the cache not holding the narinfo
|
||||
//! (`not-cached`), a request that failed or was refused (`fetch-failed`), a
|
||||
//! narinfo no signature of the key covers (`bad-signature`).
|
||||
|
|
@ -83,15 +84,17 @@ impl Cache for Http {
|
|||
}
|
||||
}
|
||||
|
||||
/// `record` when it is the latest build of the `internal/meta` commit `head`
|
||||
/// and the builder pushed it to the cache.
|
||||
/// `record` when it was built from the `internal/meta` commit `head`, or from
|
||||
/// one that pins the agent as `head` does (`same_pins`), and the builder pushed
|
||||
/// it to the cache.
|
||||
pub(crate) fn current_record<'a>(
|
||||
record: Option<&'a AgentClosure>,
|
||||
head: Option<&str>,
|
||||
same_pins: bool,
|
||||
) -> Result<&'a AgentClosure, Unpullable> {
|
||||
let record =
|
||||
record.ok_or_else(|| no(Reason::NoRecord, "no build is recorded for the agent"))?;
|
||||
if head != Some(record.meta_rev.as_str()) {
|
||||
if head != Some(record.meta_rev.as_str()) && !same_pins {
|
||||
return Err(no(
|
||||
Reason::RevMismatch,
|
||||
format!(
|
||||
|
|
@ -111,11 +114,13 @@ pub(crate) fn current_record<'a>(
|
|||
}
|
||||
|
||||
/// Whether the closure `record` names can be pulled with `facts`. `head` is the
|
||||
/// `internal/meta` commit a deploy would build from.
|
||||
/// `internal/meta` commit a deploy would build from; `same_pins` is whether the
|
||||
/// record's commit pins the agent as `head` does.
|
||||
pub(crate) async fn judge(
|
||||
facts: Option<&Facts>,
|
||||
record: Option<&AgentClosure>,
|
||||
head: Option<&str>,
|
||||
same_pins: bool,
|
||||
cache: &impl Cache,
|
||||
) -> Result<(), Unpullable> {
|
||||
let facts = facts.ok_or_else(|| {
|
||||
|
|
@ -124,7 +129,7 @@ pub(crate) async fn judge(
|
|||
"the swarm has given this hive no cache facts",
|
||||
)
|
||||
})?;
|
||||
let record = current_record(record, head)?;
|
||||
let record = current_record(record, head, same_pins)?;
|
||||
let text = match cache.narinfo(facts, &record.store_path).await {
|
||||
Ok(Some(text)) => text,
|
||||
Ok(None) => return Err(no(Reason::NotCached, "the cache does not hold the narinfo")),
|
||||
|
|
@ -196,7 +201,18 @@ async fn pass(client: &async_nats::Client) -> Result<()> {
|
|||
Some(_) => read_record(client, &spec.name).await?,
|
||||
None => None,
|
||||
};
|
||||
let verdict = judge(facts.as_ref(), record.as_ref(), head.as_deref(), &http).await;
|
||||
let same_pins = match (&record, head.as_deref()) {
|
||||
(Some(r), Some(head)) => pins_unchanged(&spec.name, &r.meta_rev, head).await,
|
||||
_ => false,
|
||||
};
|
||||
let verdict = judge(
|
||||
facts.as_ref(),
|
||||
record.as_ref(),
|
||||
head.as_deref(),
|
||||
same_pins,
|
||||
&http,
|
||||
)
|
||||
.await;
|
||||
now.push((spec.name, verdict.as_ref().err().map(|u| u.reason)));
|
||||
report(&now[now.len() - 1], verdict.err().map(|u| u.detail));
|
||||
}
|
||||
|
|
@ -206,6 +222,26 @@ async fn pass(client: &async_nats::Client) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Whether `agent` has the same pins at `built` as at `head`. A commit that
|
||||
/// cannot be read counts as different, which leaves the verdict to the commit
|
||||
/// ids.
|
||||
async fn pins_unchanged(agent: &str, built: &str, head: &str) -> bool {
|
||||
if built == head {
|
||||
return true;
|
||||
}
|
||||
let read = async {
|
||||
let archetype = crate::archetypes::of(agent)?.is_some();
|
||||
let source = crate::forge::internal_meta_source()
|
||||
.await
|
||||
.context("no forge token")?;
|
||||
crate::meta_rev::pins_unchanged(&source, agent, archetype, built, head).await
|
||||
};
|
||||
read.await.unwrap_or_else(|e| {
|
||||
tracing::debug!(%agent, error = %format!("{e:#}"), "attic: cannot compare the agent's pins");
|
||||
false
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn read_record(
|
||||
client: &async_nats::Client,
|
||||
agent: &str,
|
||||
|
|
@ -307,7 +343,7 @@ mod tests {
|
|||
head: Option<&str>,
|
||||
cache: Answers,
|
||||
) -> Option<Reason> {
|
||||
judge(facts, record, head, &cache)
|
||||
judge(facts, record, head, false, &cache)
|
||||
.await
|
||||
.err()
|
||||
.map(|u| u.reason)
|
||||
|
|
@ -345,6 +381,19 @@ mod tests {
|
|||
assert_eq!(r, Some(Reason::RevMismatch), "no head to compare with");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_record_from_another_commit_with_the_same_pins_is_pullable() {
|
||||
let (facts, record) = (facts(), record());
|
||||
let judged = judge(Some(&facts), Some(&record), Some("other"), true, &good()).await;
|
||||
assert!(judged.is_ok());
|
||||
let unpushed = AgentClosure {
|
||||
cached: false,
|
||||
..record
|
||||
};
|
||||
let judged = judge(Some(&facts), Some(&unpushed), Some("other"), true, &good()).await;
|
||||
assert_eq!(judged.err().map(|u| u.reason), Some(Reason::NotCached));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn an_unpushed_record_or_a_missing_narinfo_is_not_cached() {
|
||||
let unpushed = AgentClosure {
|
||||
|
|
|
|||
Loading…
Reference in a new issue