Skip to content

Commit b04335c

Browse files
authored
feat(gondor): cleanup gondor invocations (#1241)
* `supervisor`: instead of wrapping another python script, sanitizes the input, and exports it to systemd * `orb-hil`: instead of calling gondor, use `zorb` from the runner to query supervisor's job * `update-agent`: consume `version_overwrite`, if that is provided, overwrite `/etc/os-release` with that value. The `gondor` binary will be removed from `development` builds, and be replaced by an alias to `zorb`querying `supervisor`. This is a development feature to quickly allow developers to OTA to whatever they wish. PR is spread upon commits touching the different crates separately
1 parent 51ea892 commit b04335c

14 files changed

Lines changed: 327 additions & 250 deletions

File tree

hil/src/commands/ota/mod.rs

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,16 +109,29 @@ impl Ota {
109109
})?;
110110
info!("System time synchronized");
111111

112-
info!("Starting OTA via gondor-calls-for-ota");
113-
let start_timestamp =
114-
system::kickoff_update_agent_for_ota(&session, &self.target_version)
115-
.await
116-
.inspect_err(|e| {
117-
println!("OTA_RESULT=FAILED");
118-
println!("OTA_ERROR=OTA_KICKOFF_FAILED: {e}");
119-
})?;
112+
info!("Starting OTA");
113+
let orb_id = orb_config
114+
.orb_id
115+
.as_deref()
116+
.wrap_err("orb-id is required to kick off OTA")?;
117+
let host = self
118+
.remote
119+
.hostname
120+
.as_deref()
121+
.wrap_err("--hostname (orb IP) is required to kick off OTA")?;
122+
let start_timestamp = system::kickoff_update_agent_for_ota(
123+
&session,
124+
&self.target_version,
125+
orb_id,
126+
host,
127+
)
128+
.await
129+
.inspect_err(|e| {
130+
println!("OTA_RESULT=FAILED");
131+
println!("OTA_ERROR=OTA_KICKOFF_FAILED: {e}");
132+
})?;
120133
info!(
121-
"gondor-calls-for-ota completed, start timestamp: {}",
134+
"OTA kickoff completed, start timestamp: {}",
122135
start_timestamp
123136
);
124137

hil/src/commands/ota/system.rs

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ use color_eyre::{
44
Result,
55
};
66

7-
const GONDOR_CALLS_FOR_OTA_PATH: &str = "/usr/local/bin/gondor-calls-for-ota";
8-
97
/// Reboot the Orb device using orb-mcu-util and shutdown
108
pub async fn reboot_orb(session: &RemoteSession) -> Result<()> {
119
session
@@ -66,42 +64,37 @@ fn parse_slot_from_output(output: &str) -> Result<String> {
6664
Ok(format!("slot_{slot_letter}"))
6765
}
6866

69-
/// Kick off the update-agent flow for OTA using gondor-calls-for-ota.
70-
///
71-
/// The target is passed through verbatim; gondor itself handles stripping any
72-
/// `-{platform}-{release}` suffix, rewriting `/etc/os-release`, and restarting
73-
/// the update agent.
67+
/// Kick off the update-agent flow for OTA.
7468
pub async fn kickoff_update_agent_for_ota(
7569
session: &RemoteSession,
7670
target_version: &str,
71+
orb_id: &str,
72+
orb_ip: &str,
7773
) -> Result<String> {
7874
let start_timestamp = get_current_timestamp(session).await?;
7975

80-
let escaped_target = shell_single_quote_escape(target_version.trim());
81-
let command =
82-
format!("TERM=dumb sudo {GONDOR_CALLS_FOR_OTA_PATH} '{escaped_target}'");
83-
let result = session
84-
.execute_command(&command)
76+
let payload = serde_json::json!({
77+
"version": target_version.trim(),
78+
"restart": true,
79+
})
80+
.to_string();
81+
82+
let output = tokio::process::Command::new("zorb")
83+
.args(["-o", orb_id, "-r", orb_ip])
84+
.args(["query", "supervisor/job/gondor", &payload])
85+
.output()
8586
.await
86-
.wrap_err("Failed to execute gondor-calls-for-ota")?;
87+
.wrap_err("failed to spawn zorb on the runner")?;
8788

8889
ensure!(
89-
result.is_success(),
90-
"gondor-calls-for-ota failed: {}",
91-
if result.stderr.trim().is_empty() {
92-
result.stdout.trim()
93-
} else {
94-
result.stderr.trim()
95-
}
90+
output.status.success(),
91+
"failed to trigger OTA: {}",
92+
String::from_utf8_lossy(&output.stderr).trim()
9693
);
9794

9895
Ok(start_timestamp)
9996
}
10097

101-
fn shell_single_quote_escape(value: &str) -> String {
102-
value.replace('\'', "'\"'\"'")
103-
}
104-
10598
/// Wait for system time to be synchronized via NTP/chrony
10699
pub async fn wait_for_time_sync(session: &RemoteSession) -> Result<()> {
107100
use std::time::Duration;
@@ -207,12 +200,6 @@ pub async fn get_current_timestamp(session: &RemoteSession) -> Result<String> {
207200
mod tests {
208201
use super::*;
209202

210-
#[test]
211-
fn shell_single_quote_escape_escapes_correctly() {
212-
let escaped = shell_single_quote_escape("abc'def");
213-
assert_eq!(escaped, "abc'\"'\"'def");
214-
}
215-
216203
#[test]
217204
fn parse_chrony_reference_id_returns_id_when_synced() {
218205
let output = "Reference ID : C035676C (ptbtime1.ptb.de)\n\

orb-info/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ orb-token = [
2727
serde = ["dep:serde"]
2828

2929
[dependencies]
30-
derive_more.workspace = true
30+
derive_more = { workspace = true, features = ["display", "from_str"] }
3131
hex = { workspace = true, optional = true }
3232
orb-attest-dbus = { workspace = true, optional = true }
3333
serde = { workspace = true, optional = true, features = ["derive"] }

orb-info/src/orb_os_release.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use derive_more::Display;
1+
use derive_more::{Display, FromStr};
22
use std::collections::HashMap;
33
use thiserror::Error;
44

@@ -20,7 +20,7 @@ pub enum ReadErr {
2020
UnknownPlatformType(String),
2121
}
2222

23-
#[derive(Display, Debug, Clone, PartialEq, Eq, Copy)]
23+
#[derive(Display, FromStr, Debug, Clone, PartialEq, Eq, Copy)]
2424
pub enum OrbOsPlatform {
2525
#[display("diamond")]
2626
Diamond,
@@ -29,29 +29,29 @@ pub enum OrbOsPlatform {
2929
Pearl,
3030
}
3131

32-
#[derive(Display, Debug, Copy, Clone, PartialEq, Eq)]
32+
#[derive(Display, FromStr, Debug, Copy, Clone, PartialEq, Eq)]
3333
pub enum OrbRelease {
34+
#[display("analysis")]
35+
Analysis,
3436
#[display("dev")]
3537
Dev,
36-
#[display("service")]
37-
Service,
3838
#[display("prod")]
3939
Prod,
40+
#[display("service")]
41+
Service,
4042
#[display("stage")]
4143
Stage,
42-
#[display("analysis")]
43-
Analysis,
4444
}
4545

4646
impl OrbRelease {
4747
pub fn as_str(&self) -> &str {
4848
use OrbRelease::*;
4949
match self {
50+
Analysis => "analysis",
5051
Dev => "dev",
51-
Service => "service",
52-
Stage => "staging",
5352
Prod => "prod",
54-
Analysis => "analysis",
53+
Service => "service",
54+
Stage => "stage",
5555
}
5656
}
5757
}

supervisor/src/main.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use clap::{
33
Parser,
44
};
55
use color_eyre::eyre::WrapErr as _;
6-
use orb_info::OrbId;
6+
use orb_info::{orb_os_release::OrbOsRelease, OrbId};
77
use orb_supervisor::startup::{Application, Settings};
88
use tracing::debug;
99
use zenorb::Zenorb;
@@ -44,6 +44,9 @@ async fn main() -> color_eyre::Result<()> {
4444
debug!(?settings, "starting supervisor with settings");
4545

4646
let orb_id = OrbId::read().await.wrap_err("failed to read orb id")?;
47+
let os_release = OrbOsRelease::read()
48+
.await
49+
.wrap_err("failed to read orb os release metadata")?;
4750
let zenorb = Zenorb::from_cfg(zenorb::default_cfg())
4851
.orb_id(orb_id)
4952
.with_name("supervisor")
@@ -54,7 +57,7 @@ async fn main() -> color_eyre::Result<()> {
5457
.await
5558
.wrap_err("failed to build supervisor")?;
5659

57-
application.run().await
60+
application.run(os_release).await
5861
}
5962
.await;
6063
telemetry.flush().await;

supervisor/src/startup.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
use std::{path::PathBuf, time::Duration};
1+
use std::time::Duration;
22

33
use color_eyre::eyre::WrapErr as _;
44
use futures::{future::TryFutureExt as _, FutureExt as _};
5+
use orb_info::orb_os_release::OrbOsRelease;
56
use tracing::debug;
67
use zbus::{Connection, ConnectionBuilder};
78
use zenorb::Zenorb;
@@ -12,8 +13,11 @@ use crate::{
1213
proxies::core::{
1314
SIGNUP_PROXY_DEFAULT_OBJECT_PATH, SIGNUP_PROXY_DEFAULT_WELL_KNOWN_NAME,
1415
},
15-
tasks,
16-
tasks::zoci::GONDOR_BIN,
16+
tasks::{
17+
self,
18+
update::UPDATE_AGENT_SERVICE,
19+
zoci::{ZociContext, UPDATE_AGENT_VERSION},
20+
},
1721
};
1822

1923
pub const DBUS_WELL_KNOWN_NAME: &str = "org.worldcoin.OrbSupervisor1";
@@ -42,7 +46,6 @@ pub struct Settings {
4246
pub well_known_name: String,
4347
pub download_throttle: Duration,
4448
pub stop_core_after_signup: Duration,
45-
pub gondor_bin: PathBuf,
4649
}
4750

4851
impl Settings {
@@ -57,7 +60,6 @@ impl Settings {
5760
well_known_name: DBUS_WELL_KNOWN_NAME.to_string(),
5861
download_throttle: DEFAULT_DURATION_TO_ALLOW_DOWNLOADS,
5962
stop_core_after_signup: DURATION_TO_STOP_CORE_AFTER_LAST_SIGNUP,
60-
gondor_bin: PathBuf::from(GONDOR_BIN),
6163
}
6264
}
6365
}
@@ -149,15 +151,22 @@ impl Application {
149151
}
150152

151153
/// Runs `Application` by spawning its constituent tasks.
152-
pub async fn run(self) -> color_eyre::Result<()> {
154+
pub async fn run(self, os_release: OrbOsRelease) -> color_eyre::Result<()> {
153155
let signup_started_task =
154156
tasks::spawn_signup_started_task(&self.settings, &self.session_connection)
155157
.await?;
156158

157-
let _ =
158-
tasks::spawn_zoci_receiver(&self.zenorb, self.settings.gondor_bin.clone())
159-
.await
160-
.wrap_err("failed to spawn zoci receiver")?;
159+
let _ = tasks::spawn_zoci_receiver(
160+
&self.zenorb,
161+
ZociContext {
162+
os_release,
163+
system_conn: self.system_connection.clone(),
164+
update_agent_unit: UPDATE_AGENT_SERVICE,
165+
target_version_env: UPDATE_AGENT_VERSION,
166+
},
167+
)
168+
.await
169+
.wrap_err("failed to spawn zoci receiver")?;
161170

162171
let ((),) = tokio::try_join!(
163172
// All tasks are joined here

supervisor/src/tasks/update.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use zbus_systemd::systemd1;
1010

1111
use crate::consts::WORLDCOIN_CORE_UNIT_NAME;
1212

13+
pub const UPDATE_AGENT_SERVICE: &str = "worldcoin-update-agent.service";
14+
1315
/// Calculates the instant that is `stop_core_after_signup` after the last signup event.
1416
fn calculate_stop_deadline(
1517
last_signup_started_event: Instant,
@@ -94,7 +96,7 @@ pub fn spawn_start_update_agent_after_core_shutdown_task(
9496
}
9597
info!("calling `org.freedesktop.systemd1.Manager.StartUnit` to start update agent");
9698
proxy
97-
.start_unit("worldcoin-update-agent.service".into(), "replace".into())
99+
.start_unit(UPDATE_AGENT_SERVICE.into(), "replace".into())
98100
.await
99101
.map(|_| {})
100102
.map_err(Into::into)

0 commit comments

Comments
 (0)