Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updog: nice JSON and better waves #539

Merged
merged 4 commits into from
Dec 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 66 additions & 19 deletions workspaces/updater/update_metadata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,37 @@ use std::ops::Bound::{Excluded, Included};

pub const MAX_SEED: u32 = 2048;

#[derive(Debug, PartialEq, Eq)]
pub enum Wave {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this name, should we rename Update.waves to Update.seed_start_times or something? Or, could go further and introduce knowledge of seed to Wave and remove the need for the map? (I lean toward a rename)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could, but it would be an internal-only change unless we want to add this to the list of reasons to have a manifest format change. I'll leave this out of this change and have a think about it.

Initial {
end: DateTime<Utc>,
},
General {
start: DateTime<Utc>,
end: DateTime<Utc>,
},
Last {
start: DateTime<Utc>,
},
}

impl Wave {
pub fn has_started(&self) -> bool {
match self {
Self::Initial { .. } => true,
Self::General { start, .. } | Self::Last { start } => *start <= Utc::now(),
}
}

pub fn has_passed(&self) -> bool {
match self {
Self::Initial { end } => *end <= Utc::now(),
Self::General { end, .. } => *end <= Utc::now(),
Self::Last { start } => *start <= Utc::now(),
}
}
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Images {
pub boot: String,
Expand Down Expand Up @@ -42,40 +73,56 @@ pub struct Manifest {
}

impl Update {
pub fn update_wave(&self, seed: u32) -> Option<&DateTime<Utc>> {
self.waves
.range((Included(0), Included(seed)))
/// Returns the update wave that Updog belongs to, based on the seed value.
/// Depending on the waves described in the update, the possible results are
tjkirch marked this conversation as resolved.
Show resolved Hide resolved
/// - Some wave described by a start and end time.
/// - The "0th" wave, which has an "end" time but no specified start time.
/// - The last wave, which has a start time but no specified end time.
/// - Nothing, if no waves are configured.
pub fn update_wave(&self, seed: u32) -> Option<Wave> {
let start = self
.waves
.range((Included(0), Excluded(seed)))
.last()
.map(|(_, wave)| wave)
.map(|(_, wave)| *wave);
let end = self
.waves
.range((Included(seed), Included(MAX_SEED)))
.next()
.map(|(_, wave)| *wave);

match (start, end) {
(None, Some(end)) => Some(Wave::Initial { end }),
(Some(start), Some(end)) => Some(Wave::General { start, end }),
(Some(start), None) => Some(Wave::Last { start }),
_ => None,
}
}

pub fn update_ready(&self, seed: u32) -> bool {
// Has this client's wave started
if let Some(wave) = self.update_wave(seed) {
return *wave <= Utc::now();
}

// Alternately have all waves passed
if let Some((_, wave)) = self.waves.iter().last() {
return *wave <= Utc::now();
return wave.has_started();
}

// Or there are no waves
true
}

pub fn jitter(&self, seed: u32) -> Option<DateTime<Utc>> {
let prev = self.update_wave(seed);
let next = self
.waves
.range((Excluded(seed), Excluded(MAX_SEED)))
.next()
.map(|(_, wave)| wave);
if let (Some(start), Some(end)) = (prev, next) {
if Utc::now() < *end {
if let Some(wave) = self.update_wave(seed) {
if wave.has_passed() {
return None;
}
let bounds = match self.update_wave(seed) {
Some(Wave::Initial { end }) => Some((Utc::now(), end)),
Some(Wave::General { start, end }) => Some((start, end)),
Some(Wave::Last { start: _ }) | None => None,
};
if let Some((start, end)) = bounds {
let mut rng = thread_rng();
if let Some(range) = end.timestamp().checked_sub(start.timestamp()) {
return Some(*start + Duration::seconds(rng.gen_range(1, range)));
return Some(start + Duration::seconds(rng.gen_range(1, range)));
}
}
}
Expand Down
Loading