omaha_client/state_machine/
update_check.rs1use crate::{
12 common::{ProtocolState, UpdateCheckSchedule, UserCounting},
13 protocol::Cohort,
14 storage::{Storage, StorageExt},
15 time::PartialComplexTime,
16};
17use log::error;
18use std::convert::{TryFrom, TryInto};
19use std::time::Duration;
20
21pub const CONSECUTIVE_FAILED_UPDATE_CHECKS: &str = "consecutive_failed_update_checks";
23pub const LAST_UPDATE_TIME: &str = "last_update_time";
24pub const SERVER_DICTATED_POLL_INTERVAL: &str = "server_dictated_poll_interval";
25
26#[derive(Clone, Debug)]
31pub struct Context {
32 pub schedule: UpdateCheckSchedule,
34
35 pub state: ProtocolState,
38}
39
40impl Context {
41 pub async fn load(storage: &impl Storage) -> Self {
43 let last_update_time =
44 storage.get_time(LAST_UPDATE_TIME).await.map(PartialComplexTime::Wall);
45 let server_dictated_poll_interval = storage
46 .get_int(SERVER_DICTATED_POLL_INTERVAL)
47 .await
48 .and_then(|t| u64::try_from(t).ok())
49 .map(Duration::from_micros);
50
51 let consecutive_failed_update_checks: u32 = storage
52 .get_int(CONSECUTIVE_FAILED_UPDATE_CHECKS)
53 .await
54 .unwrap_or(0)
55 .try_into()
56 .unwrap_or_default();
57
58 Context {
61 schedule: UpdateCheckSchedule::builder()
62 .last_update_time(last_update_time)
63 .last_update_check_time(last_update_time)
64 .build(),
65 state: ProtocolState {
66 server_dictated_poll_interval,
67 consecutive_failed_update_checks,
68 ..Default::default()
69 },
70 }
71 }
72
73 pub async fn persist<'a>(&'a self, storage: &'a mut impl Storage) {
77 if let Err(e) = storage
78 .set_option_int(
79 LAST_UPDATE_TIME,
80 self.schedule
81 .last_update_time
82 .and_then(PartialComplexTime::checked_to_micros_since_epoch),
83 )
84 .await
85 {
86 error!("Unable to persist {}: {}", LAST_UPDATE_TIME, e);
87 }
88
89 if let Err(e) = storage
90 .set_option_int(
91 SERVER_DICTATED_POLL_INTERVAL,
92 self.state
93 .server_dictated_poll_interval
94 .map(|t| t.as_micros())
95 .and_then(|t| i64::try_from(t).ok()),
96 )
97 .await
98 {
99 error!("Unable to persist {}: {}", SERVER_DICTATED_POLL_INTERVAL, e);
100 }
101
102 let consecutive_failed_update_checks_option = {
105 if self.state.consecutive_failed_update_checks == 0 {
106 None
107 } else {
108 Some(self.state.consecutive_failed_update_checks as i64)
109 }
110 };
111
112 if let Err(e) = storage
113 .set_option_int(
114 CONSECUTIVE_FAILED_UPDATE_CHECKS,
115 consecutive_failed_update_checks_option,
116 )
117 .await
118 {
119 error!("Unable to persist {}: {}", CONSECUTIVE_FAILED_UPDATE_CHECKS, e);
120 }
121 }
122}
123
124#[derive(Debug)]
127pub struct Response {
128 pub app_responses: Vec<AppResponse>,
130}
131
132#[derive(Debug)]
135pub struct AppResponse {
136 pub app_id: String,
138
139 pub cohort: Cohort,
141
142 pub user_counting: UserCounting,
143
144 pub result: Action,
146}
147
148#[derive(Debug, Clone, PartialEq)]
153pub enum Action {
154 NoUpdate,
156
157 DeferredByPolicy,
160
161 DeniedByPolicy,
164
165 InstallPlanExecutionError,
168
169 Updated,
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176 use crate::storage::MemStorage;
177 use futures::executor::block_on;
178
179 #[test]
180 fn test_load_context() {
181 block_on(async {
182 let mut storage = MemStorage::new();
183 let last_update_time = 123456789;
184 let poll_interval = Duration::from_micros(56789u64);
185 storage.set_int(LAST_UPDATE_TIME, last_update_time).await.unwrap();
186 storage
187 .set_int(SERVER_DICTATED_POLL_INTERVAL, poll_interval.as_micros() as i64)
188 .await
189 .unwrap();
190
191 storage.set_int(CONSECUTIVE_FAILED_UPDATE_CHECKS, 1234).await.unwrap();
192
193 let context = Context::load(&storage).await;
194
195 let last_update_time = PartialComplexTime::from_micros_since_epoch(last_update_time);
196 assert_eq!(context.schedule.last_update_time, Some(last_update_time));
197 assert_eq!(context.state.server_dictated_poll_interval, Some(poll_interval));
198 assert_eq!(context.state.consecutive_failed_update_checks, 1234);
199 });
200 }
201
202 #[test]
203 fn test_load_context_empty_storage() {
204 block_on(async {
205 let storage = MemStorage::new();
206 let context = Context::load(&storage).await;
207 assert_eq!(None, context.schedule.last_update_time);
208 assert_eq!(None, context.state.server_dictated_poll_interval);
209 assert_eq!(0, context.state.consecutive_failed_update_checks);
210 });
211 }
212
213 #[test]
214 fn test_persist_context() {
215 block_on(async {
216 let mut storage = MemStorage::new();
217 let last_update_time = PartialComplexTime::from_micros_since_epoch(123456789);
218 let server_dictated_poll_interval = Some(Duration::from_micros(56789));
219 let consecutive_failed_update_checks = 1234;
220 let context = Context {
221 schedule: UpdateCheckSchedule::builder().last_update_time(last_update_time).build(),
222 state: ProtocolState {
223 server_dictated_poll_interval,
224 consecutive_failed_update_checks,
225 ..ProtocolState::default()
226 },
227 };
228 context.persist(&mut storage).await;
229 assert_eq!(Some(123456789), storage.get_int(LAST_UPDATE_TIME).await);
230 assert_eq!(Some(56789), storage.get_int(SERVER_DICTATED_POLL_INTERVAL).await);
231 assert_eq!(Some(1234), storage.get_int(CONSECUTIVE_FAILED_UPDATE_CHECKS).await);
232 assert!(!storage.committed());
233 });
234 }
235
236 #[test]
237 fn test_persist_context_remove_defaults() {
238 block_on(async {
239 let mut storage = MemStorage::new();
240 let last_update_time = PartialComplexTime::from_micros_since_epoch(123456789);
241 storage.set_int(SERVER_DICTATED_POLL_INTERVAL, 987654).await.unwrap();
242 storage.set_int(CONSECUTIVE_FAILED_UPDATE_CHECKS, 1234).await.unwrap();
243
244 let context = Context {
245 schedule: UpdateCheckSchedule::builder().last_update_time(last_update_time).build(),
246 state: ProtocolState {
247 server_dictated_poll_interval: None,
248 consecutive_failed_update_checks: 0,
249 ..ProtocolState::default()
250 },
251 };
252 context.persist(&mut storage).await;
253 assert_eq!(Some(123456789), storage.get_int(LAST_UPDATE_TIME).await);
254 assert_eq!(None, storage.get_int(SERVER_DICTATED_POLL_INTERVAL).await);
255 assert_eq!(None, storage.get_int(CONSECUTIVE_FAILED_UPDATE_CHECKS).await);
256 assert!(!storage.committed());
257 });
258 }
259}