nixfleet_agent/activation/
verify_poll.rs1use std::time::Duration;
4
5use anyhow::{Context, Result, anyhow};
6
7use super::types::{POLL_BUDGET, POLL_INTERVAL};
8
9pub async fn read_current_system_basename() -> Result<String> {
10 let target = tokio::fs::read_link("/run/current-system")
11 .await
12 .with_context(|| "readlink /run/current-system")?;
13 let basename = target
14 .file_name()
15 .and_then(|n| n.to_str())
16 .ok_or_else(|| {
17 anyhow!(
18 "/run/current-system target has no utf-8 basename: {}",
19 target.display()
20 )
21 })?
22 .to_string();
23 Ok(basename)
24}
25
26#[derive(Debug, Clone)]
27pub enum PollOutcome {
28 Settled,
29 Timeout {
31 last_observed: String,
32 },
33 FlippedToUnexpected {
36 observed: String,
37 },
38}
39
40pub struct VerifyPoll<'a> {
44 pub expected_basename: &'a str,
45 pub previous_basename: Option<&'a str>,
46 pub interval: Duration,
47 pub budget: Duration,
48}
49
50impl<'a> VerifyPoll<'a> {
51 pub fn new(expected_basename: &'a str) -> Self {
52 Self {
53 expected_basename,
54 previous_basename: None,
55 interval: POLL_INTERVAL,
56 budget: POLL_BUDGET,
57 }
58 }
59
60 pub fn with_previous(mut self, previous: &'a str) -> Self {
61 self.previous_basename = Some(previous);
62 self
63 }
64
65 pub async fn until_settled(&self) -> PollOutcome {
67 let deadline = tokio::time::Instant::now() + self.budget;
68 #[allow(unused_assignments)]
70 let mut last_observed: Option<String> = None;
71
72 loop {
73 match read_current_system_basename().await {
74 Ok(basename) => {
75 if basename == self.expected_basename {
76 return PollOutcome::Settled;
77 }
78 if let Some(prev) = self.previous_basename
79 && basename != prev
80 {
81 return PollOutcome::FlippedToUnexpected { observed: basename };
82 }
83 last_observed = Some(basename);
84 }
85 Err(err) => {
86 last_observed = Some(format!("<read-error: {err}>"));
88 }
89 }
90
91 if tokio::time::Instant::now() >= deadline {
92 return PollOutcome::Timeout {
93 last_observed: last_observed
94 .unwrap_or_else(|| String::from("<no-reads-completed>")),
95 };
96 }
97 tokio::time::sleep(self.interval).await;
98 }
99 }
100}