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 = storage
44 .get_time(LAST_UPDATE_TIME)
45 .await
46 .map(PartialComplexTime::Wall);
47 let server_dictated_poll_interval = storage
48 .get_int(SERVER_DICTATED_POLL_INTERVAL)
49 .await
50 .and_then(|t| u64::try_from(t).ok())
51 .map(Duration::from_micros);
52
53 let consecutive_failed_update_checks: u32 = storage
54 .get_int(CONSECUTIVE_FAILED_UPDATE_CHECKS)
55 .await
56 .unwrap_or(0)
57 .try_into()
58 .unwrap_or_default();
59
60 Context {
63 schedule: UpdateCheckSchedule::builder()
64 .last_update_time(last_update_time)
65 .last_update_check_time(last_update_time)
66 .build(),
67 state: ProtocolState {
68 server_dictated_poll_interval,
69 consecutive_failed_update_checks,
70 ..Default::default()
71 },
72 }
73 }
74
75 pub async fn persist<'a>(&'a self, storage: &'a mut impl Storage) {
79 if let Err(e) = storage
80 .set_option_int(
81 LAST_UPDATE_TIME,
82 self.schedule
83 .last_update_time
84 .and_then(PartialComplexTime::checked_to_micros_since_epoch),
85 )
86 .await
87 {
88 error!("Unable to persist {}: {}", LAST_UPDATE_TIME, e);
89 }
90
91 if let Err(e) = storage
92 .set_option_int(
93 SERVER_DICTATED_POLL_INTERVAL,
94 self.state
95 .server_dictated_poll_interval
96 .map(|t| t.as_micros())
97 .and_then(|t| i64::try_from(t).ok()),
98 )
99 .await
100 {
101 error!("Unable to persist {}: {}", SERVER_DICTATED_POLL_INTERVAL, e);
102 }
103
104 let consecutive_failed_update_checks_option = {
107 if self.state.consecutive_failed_update_checks == 0 {
108 None
109 } else {
110 Some(self.state.consecutive_failed_update_checks as i64)
111 }
112 };
113
114 if let Err(e) = storage
115 .set_option_int(
116 CONSECUTIVE_FAILED_UPDATE_CHECKS,
117 consecutive_failed_update_checks_option,
118 )
119 .await
120 {
121 error!(
122 "Unable to persist {}: {}",
123 CONSECUTIVE_FAILED_UPDATE_CHECKS, e
124 );
125 }
126 }
127}
128
129#[derive(Debug)]
132pub struct Response {
133 pub app_responses: Vec<AppResponse>,
135}
136
137#[derive(Debug)]
140pub struct AppResponse {
141 pub app_id: String,
143
144 pub cohort: Cohort,
146
147 pub user_counting: UserCounting,
148
149 pub result: Action,
151}
152
153#[derive(Debug, Clone, PartialEq)]
158pub enum Action {
159 NoUpdate,
161
162 DeferredByPolicy,
165
166 DeniedByPolicy,
169
170 InstallPlanExecutionError,
173
174 Updated,
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 use crate::storage::MemStorage;
182 use futures::executor::block_on;
183
184 #[test]
185 fn test_load_context() {
186 block_on(async {
187 let mut storage = MemStorage::new();
188 let last_update_time = 123456789;
189 let poll_interval = Duration::from_micros(56789u64);
190 storage
191 .set_int(LAST_UPDATE_TIME, last_update_time)
192 .await
193 .unwrap();
194 storage
195 .set_int(
196 SERVER_DICTATED_POLL_INTERVAL,
197 poll_interval.as_micros() as i64,
198 )
199 .await
200 .unwrap();
201
202 storage
203 .set_int(CONSECUTIVE_FAILED_UPDATE_CHECKS, 1234)
204 .await
205 .unwrap();
206
207 let context = Context::load(&storage).await;
208
209 let last_update_time = PartialComplexTime::from_micros_since_epoch(last_update_time);
210 assert_eq!(context.schedule.last_update_time, Some(last_update_time));
211 assert_eq!(
212 context.state.server_dictated_poll_interval,
213 Some(poll_interval)
214 );
215 assert_eq!(context.state.consecutive_failed_update_checks, 1234);
216 });
217 }
218
219 #[test]
220 fn test_load_context_empty_storage() {
221 block_on(async {
222 let storage = MemStorage::new();
223 let context = Context::load(&storage).await;
224 assert_eq!(None, context.schedule.last_update_time);
225 assert_eq!(None, context.state.server_dictated_poll_interval);
226 assert_eq!(0, context.state.consecutive_failed_update_checks);
227 });
228 }
229
230 #[test]
231 fn test_persist_context() {
232 block_on(async {
233 let mut storage = MemStorage::new();
234 let last_update_time = PartialComplexTime::from_micros_since_epoch(123456789);
235 let server_dictated_poll_interval = Some(Duration::from_micros(56789));
236 let consecutive_failed_update_checks = 1234;
237 let context = Context {
238 schedule: UpdateCheckSchedule::builder()
239 .last_update_time(last_update_time)
240 .build(),
241 state: ProtocolState {
242 server_dictated_poll_interval,
243 consecutive_failed_update_checks,
244 ..ProtocolState::default()
245 },
246 };
247 context.persist(&mut storage).await;
248 assert_eq!(Some(123456789), storage.get_int(LAST_UPDATE_TIME).await);
249 assert_eq!(
250 Some(56789),
251 storage.get_int(SERVER_DICTATED_POLL_INTERVAL).await
252 );
253 assert_eq!(
254 Some(1234),
255 storage.get_int(CONSECUTIVE_FAILED_UPDATE_CHECKS).await
256 );
257 assert!(!storage.committed());
258 });
259 }
260
261 #[test]
262 fn test_persist_context_remove_defaults() {
263 block_on(async {
264 let mut storage = MemStorage::new();
265 let last_update_time = PartialComplexTime::from_micros_since_epoch(123456789);
266 storage
267 .set_int(SERVER_DICTATED_POLL_INTERVAL, 987654)
268 .await
269 .unwrap();
270 storage
271 .set_int(CONSECUTIVE_FAILED_UPDATE_CHECKS, 1234)
272 .await
273 .unwrap();
274
275 let context = Context {
276 schedule: UpdateCheckSchedule::builder()
277 .last_update_time(last_update_time)
278 .build(),
279 state: ProtocolState {
280 server_dictated_poll_interval: None,
281 consecutive_failed_update_checks: 0,
282 ..ProtocolState::default()
283 },
284 };
285 context.persist(&mut storage).await;
286 assert_eq!(Some(123456789), storage.get_int(LAST_UPDATE_TIME).await);
287 assert_eq!(None, storage.get_int(SERVER_DICTATED_POLL_INTERVAL).await);
288 assert_eq!(
289 None,
290 storage.get_int(CONSECUTIVE_FAILED_UPDATE_CHECKS).await
291 );
292 assert!(!storage.committed());
293 });
294 }
295}