feat: add client_id to UpdaterState (#70)
* feat: add client_id to UpdaterState * Ensure we save UpdaterState after it's created * Save state if we assign it a client id on load * Rename
This commit is contained in:
@@ -42,6 +42,9 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0.93"
|
||||
# For reading shorebird.yaml
|
||||
serde_yaml = "0.9.19"
|
||||
uuid = { version = "1.4.1", features = [
|
||||
"v4", # To generate random UUIDs
|
||||
] }
|
||||
# For computing hashes of patch files for validation.
|
||||
sha2 = "0.10.6"
|
||||
# For decompressing .apk files.
|
||||
|
||||
+55
-6
@@ -34,7 +34,7 @@ struct Slot {
|
||||
|
||||
// This struct is public, as callers can have a handle to it, but modifying
|
||||
// anything inside should be done via the functions below.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct UpdaterState {
|
||||
/// Where this writes to disk.
|
||||
cache_dir: PathBuf,
|
||||
@@ -53,6 +53,8 @@ pub struct UpdaterState {
|
||||
next_boot_slot_index: Option<usize>,
|
||||
/// List of slots.
|
||||
slots: Vec<Slot>,
|
||||
/// The client ID for this device.
|
||||
client_id: Option<String>,
|
||||
// Add file path or FD so modifying functions can save it to disk?
|
||||
}
|
||||
|
||||
@@ -61,6 +63,7 @@ impl UpdaterState {
|
||||
Self {
|
||||
cache_dir,
|
||||
release_version,
|
||||
client_id: Some(generate_client_id()),
|
||||
current_boot_slot_index: None,
|
||||
next_boot_slot_index: None,
|
||||
failed_patches: Vec::new(),
|
||||
@@ -79,6 +82,10 @@ fn is_file_not_found(error: &anyhow::Error) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn generate_client_id() -> String {
|
||||
uuid::Uuid::new_v4().to_string()
|
||||
}
|
||||
|
||||
impl UpdaterState {
|
||||
pub fn is_known_good_patch(&self, patch_number: usize) -> bool {
|
||||
self.successful_patches.iter().any(|v| v == &patch_number)
|
||||
@@ -121,10 +128,21 @@ impl UpdaterState {
|
||||
let reader = BufReader::new(file);
|
||||
// TODO: Now that we depend on serde_yaml for shorebird.yaml
|
||||
// we could use yaml here instead of json.
|
||||
let state = serde_json::from_reader(reader)?;
|
||||
let mut state: UpdaterState = serde_json::from_reader(reader)?;
|
||||
if state.client_id.is_none() {
|
||||
// Generate a client id if we don't already have one.
|
||||
state.client_id = Some(generate_client_id());
|
||||
let _ = state.save();
|
||||
}
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
fn create_new_and_save(cache_dir: &Path, release_version: &str) -> Self {
|
||||
let state = Self::new(cache_dir.to_owned(), release_version.to_owned());
|
||||
let _ = state.save();
|
||||
state
|
||||
}
|
||||
|
||||
pub fn load_or_new_on_error(cache_dir: &Path, release_version: &str) -> Self {
|
||||
let load_result = Self::load(cache_dir);
|
||||
match load_result {
|
||||
@@ -134,12 +152,12 @@ impl UpdaterState {
|
||||
"release_version changed {} -> {}, clearing updater state",
|
||||
loaded.release_version, release_version
|
||||
);
|
||||
return Self::new(cache_dir.to_owned(), release_version.to_owned());
|
||||
return Self::create_new_and_save(cache_dir, release_version);
|
||||
}
|
||||
let validate_result = loaded.validate();
|
||||
if let Err(e) = validate_result {
|
||||
warn!("Error while validating state: {:#}, clearing state.", e);
|
||||
return Self::new(cache_dir.to_owned(), release_version.to_owned());
|
||||
return Self::create_new_and_save(cache_dir, release_version);
|
||||
}
|
||||
loaded
|
||||
}
|
||||
@@ -147,13 +165,13 @@ impl UpdaterState {
|
||||
if !is_file_not_found(&e) {
|
||||
warn!("Error loading state: {:#}, clearing state.", e);
|
||||
}
|
||||
Self::new(cache_dir.to_owned(), release_version.to_owned())
|
||||
return Self::create_new_and_save(cache_dir, release_version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Saves the updater state to disk.
|
||||
pub fn save(&self) -> anyhow::Result<()> {
|
||||
// Save UpdaterState to disk
|
||||
std::fs::create_dir_all(&self.cache_dir).context("create_dir_all")?;
|
||||
let path = Path::new(&self.cache_dir).join("state.json");
|
||||
let file = File::create(path).context("File::create for state.json")?;
|
||||
@@ -514,4 +532,35 @@ mod tests {
|
||||
assert!(result.is_err());
|
||||
assert!(super::is_file_not_found(&result.unwrap_err()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn creates_updater_state_with_client_id() {
|
||||
let tmp_dir = TempDir::new("example").unwrap();
|
||||
let state = UpdaterState::load_or_new_on_error(&tmp_dir.path().to_path_buf(), "1.0.0+1");
|
||||
assert!(state.client_id.is_some());
|
||||
let saved_state =
|
||||
UpdaterState::load_or_new_on_error(&tmp_dir.path().to_path_buf(), "1.0.0+1");
|
||||
assert_eq!(state.client_id, saved_state.client_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adds_client_id_to_saved_state() {
|
||||
let tmp_dir = TempDir::new("example").unwrap();
|
||||
let state: UpdaterState = UpdaterState {
|
||||
cache_dir: tmp_dir.path().to_path_buf(),
|
||||
release_version: "1.0.0+1".to_string(),
|
||||
client_id: None,
|
||||
current_boot_slot_index: None,
|
||||
next_boot_slot_index: None,
|
||||
failed_patches: Vec::new(),
|
||||
successful_patches: Vec::new(),
|
||||
slots: Vec::new(),
|
||||
};
|
||||
|
||||
state.save().unwrap();
|
||||
|
||||
let loaded_state =
|
||||
UpdaterState::load_or_new_on_error(&state.cache_dir, &state.release_version);
|
||||
assert!(loaded_state.client_id.is_some());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user