diff options
author | Shaun Taheri <shaun@advancedtelematic.com> | 2016-09-09 12:35:01 +0200 |
---|---|---|
committer | Shaun Taheri <shaun@advancedtelematic.com> | 2016-09-09 14:56:53 +0200 |
commit | 3b56557d70893122575be2bd4422e5b5d65f7f7f (patch) | |
tree | b206b1a780c1f2e88e1777540fcf4e2084456471 | |
parent | 472109e26e883463c9540b3ec9221daf50311f4f (diff) | |
download | rvi_sota_client-3b56557d70893122575be2bd4422e5b5d65f7f7f.tar.gz |
Handle the returned UpdateRequestStatus from Core
-rw-r--r-- | Cargo.toml | 6 | ||||
-rw-r--r-- | docs/client-guide.adoc | 2 | ||||
-rw-r--r-- | src/datatype/command.rs | 38 | ||||
-rw-r--r-- | src/datatype/config.rs | 8 | ||||
-rw-r--r-- | src/datatype/event.rs | 19 | ||||
-rw-r--r-- | src/datatype/mod.rs | 7 | ||||
-rw-r--r-- | src/datatype/update_request.rs (renamed from src/datatype/package.rs) | 59 | ||||
-rw-r--r-- | src/gateway/dbus.rs | 2 | ||||
-rw-r--r-- | src/gateway/socket.rs | 10 | ||||
-rw-r--r-- | src/http/auth_client.rs | 3 | ||||
-rw-r--r-- | src/interpreter.rs | 63 | ||||
-rw-r--r-- | src/main.rs | 2 | ||||
-rw-r--r-- | src/package_manager/package_manager.rs | 6 | ||||
-rw-r--r-- | src/rvi/parameters.rs | 2 | ||||
-rw-r--r-- | src/sota.rs | 32 | ||||
-rw-r--r-- | tests/sota.toml | 4 |
16 files changed, 109 insertions, 154 deletions
@@ -34,9 +34,3 @@ toml = "0.1.30" unix_socket = "0.5.0" url = "1.1.1" ws = "0.5.0" - -[profile.dev] -panic = 'abort' - -[profile.release] -panic = 'abort' diff --git a/docs/client-guide.adoc b/docs/client-guide.adoc index bda8174..b5dab43 100644 --- a/docs/client-guide.adoc +++ b/docs/client-guide.adoc @@ -86,7 +86,7 @@ http_server = "127.0.0.1:8080" <1> socket_commands_path = "/tmp/sota-commands.socket" <2> socket_events_path = "/tmp/sota-events.socket" <3> websocket_server = "https://sota-core.gw.prod01.advancedtelematic.com" <4> -rvi_edge_server = "http://127.0.0.1:9080" <5> +rvi_edge_server = "127.0.0.1:9080" <5> ---- <1> The path to the http-only core server, if the http gateway is enabled in the [gateway] section. <2> The name of the unix domain socket to be used for sending commands, if the socket gateway is enabled in the [gateway] section. diff --git a/src/datatype/command.rs b/src/datatype/command.rs index 11707ea..71567da 100644 --- a/src/datatype/command.rs +++ b/src/datatype/command.rs @@ -15,10 +15,8 @@ pub enum Command { /// Shutdown the client immediately. Shutdown, - /// Check for any pending updates. - GetPendingUpdates, - /// Check for any in-flight updates. - GetInFlightUpdates, + /// Check for any pending or in-flight updates. + GetUpdateRequests, /// List the installed packages on the system. ListInstalledPackages, @@ -67,10 +65,8 @@ named!(command <(Command, Vec<&str>)>, chain!( ~ cmd: alt!( alt_complete!(tag!("Authenticate") | tag!("auth")) => { |_| Command::Authenticate(None) } - | alt_complete!(tag!("GetPendingUpdates") | tag!("getpend")) - => { |_| Command::GetPendingUpdates } - | alt_complete!(tag!("GetInFlightUpdates") | tag!("getflight")) - => { |_| Command::GetInFlightUpdates } + | alt_complete!(tag!("GetUpdateRequests") | tag!("getreq")) + => { |_| Command::GetUpdateRequests } | alt_complete!(tag!("ListInstalledPackages") | tag!("ls")) => { |_| Command::ListInstalledPackages } | alt_complete!(tag!("ListSystemInfo") | tag!("info")) @@ -120,14 +116,9 @@ fn parse_arguments(cmd: Command, args: Vec<&str>) -> Result<Command, Error> { _ => Err(Error::Command(format!("unexpected Authenticate args: {:?}", args))), }, - Command::GetPendingUpdates => match args.len() { - 0 => Ok(Command::GetPendingUpdates), - _ => Err(Error::Command(format!("unexpected GetPendingUpdates args: {:?}", args))), - }, - - Command::GetInFlightUpdates => match args.len() { - 0 => Ok(Command::GetInFlightUpdates), - _ => Err(Error::Command(format!("unexpected GetInFlightUpdates args: {:?}", args))), + Command::GetUpdateRequests => match args.len() { + 0 => Ok(Command::GetUpdateRequests), + _ => Err(Error::Command(format!("unexpected GetUpdateRequests args: {:?}", args))), }, Command::ListInstalledPackages => match args.len() { @@ -241,17 +232,10 @@ mod tests { } #[test] - fn get_pending_updates_test() { - assert_eq!("GetPendingUpdates".parse::<Command>().unwrap(), Command::GetPendingUpdates); - assert_eq!("getpend".parse::<Command>().unwrap(), Command::GetPendingUpdates); - assert!("getpend old".parse::<Command>().is_err()); - } - - #[test] - fn get_in_flight_updates_test() { - assert_eq!("GetInFlightUpdates".parse::<Command>().unwrap(), Command::GetInFlightUpdates); - assert_eq!("getflight".parse::<Command>().unwrap(), Command::GetInFlightUpdates); - assert!("getflight old".parse::<Command>().is_err()); + fn get_update_requests_test() { + assert_eq!("GetUpdateRequests".parse::<Command>().unwrap(), Command::GetUpdateRequests); + assert_eq!("getreq".parse::<Command>().unwrap(), Command::GetUpdateRequests); + assert!("getreq now".parse::<Command>().is_err()); } #[test] diff --git a/src/datatype/config.rs b/src/datatype/config.rs index 650008b..c9c7708 100644 --- a/src/datatype/config.rs +++ b/src/datatype/config.rs @@ -262,8 +262,8 @@ pub struct NetworkConfig { impl Default for NetworkConfig { fn default() -> NetworkConfig { NetworkConfig { - http_server: "http://127.0.0.1:8888".to_string(), - rvi_edge_server: "http://127.0.0.1:9080".to_string(), + http_server: "127.0.0.1:8888".to_string(), + rvi_edge_server: "127.0.0.1:9080".to_string(), socket_commands_path: "/tmp/sota-commands.socket".to_string(), socket_events_path: "/tmp/sota-events.socket".to_string(), websocket_server: "127.0.0.1:3012".to_string() @@ -348,8 +348,8 @@ mod tests { const NETWORK_CONFIG: &'static str = r#" [network] - http_server = "http://127.0.0.1:8888" - rvi_edge_server = "http://127.0.0.1:9080" + http_server = "127.0.0.1:8888" + rvi_edge_server = "127.0.0.1:9080" socket_commands_path = "/tmp/sota-commands.socket" socket_events_path = "/tmp/sota-events.socket" websocket_server = "127.0.0.1:3012" diff --git a/src/datatype/event.rs b/src/datatype/event.rs index 6a493f0..2d6f895 100644 --- a/src/datatype/event.rs +++ b/src/datatype/event.rs @@ -1,7 +1,7 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; -use datatype::{DownloadComplete, Package, PendingUpdateRequest, UpdateAvailable, - UpdateReport, UpdateRequestId}; +use datatype::{DownloadComplete, Package, UpdateAvailable, UpdateReport, + UpdateRequest, UpdateRequestId}; /// System-wide events that are broadcast to all interested parties. @@ -15,17 +15,12 @@ pub enum Event { /// An operation failed because we are not currently authenticated. NotAuthenticated, - /// There are pending updates available. - PendingUpdatesReceived(Vec<UpdateRequestId>), + /// A notification from Core of pending or in-flight updates. + UpdatesReceived(Vec<UpdateRequest>), /// A notification from RVI of a pending update. - PendingUpdateAvailable(UpdateAvailable), - /// There are no pending updates. - NoPendingUpdates, - - /// There are in-flight updates available. - InFlightUpdatesReceived(Vec<PendingUpdateRequest>), - /// There are no in-flight updates. - NoInFlightUpdates, + UpdateAvailable(UpdateAvailable), + /// There are no outstanding update requests. + NoUpdateRequests, /// The following packages are installed on the device. FoundInstalledPackages(Vec<Package>), diff --git a/src/datatype/mod.rs b/src/datatype/mod.rs index 8a9ca4e..6516422 100644 --- a/src/datatype/mod.rs +++ b/src/datatype/mod.rs @@ -5,9 +5,9 @@ pub mod dbus; pub mod error; pub mod event; pub mod json_rpc; -pub mod package; pub mod system_info; pub mod update_report; +pub mod update_request; pub mod url; pub use self::auth::{AccessToken, Auth, ClientId, ClientSecret, ClientCredentials}; @@ -17,10 +17,11 @@ pub use self::config::{AuthConfig, CoreConfig, Config, DBusConfig, DeviceConfig, pub use self::error::Error; pub use self::event::Event; pub use self::json_rpc::{RpcRequest, RpcOk, RpcErr}; -pub use self::package::{ChunkReceived, DownloadStarted, DownloadComplete, Package, - PendingUpdateRequest, UpdateAvailable, UpdateRequestId}; pub use self::system_info::SystemInfo; pub use self::update_report::{DeviceReport, InstalledFirmware, InstalledPackage, InstalledSoftware, OperationResult, UpdateResultCode, UpdateReport}; +pub use self::update_request::{ChunkReceived, DownloadComplete, DownloadFailed, + DownloadStarted, Package, UpdateAvailable, + UpdateRequest, UpdateRequestId, UpdateRequestStatus}; pub use self::url::{Method, Url}; diff --git a/src/datatype/package.rs b/src/datatype/update_request.rs index 146ff06..7398848 100644 --- a/src/datatype/package.rs +++ b/src/datatype/update_request.rs @@ -3,14 +3,36 @@ use std::fmt::{Display, Formatter, Result as FmtResult}; use rvi::services::LocalServices; -/// Encapsulate a `String` type used to represent the `Package` version. -pub type Version = String; +/// Encapsulate a `String` type as the id of a specific update request. +pub type UpdateRequestId = String; + +/// A device update request from Core to be installed by the client. +#[allow(non_snake_case)] +#[derive(RustcDecodable, RustcEncodable, PartialEq, Eq, Debug, Clone)] +pub struct UpdateRequest { + pub requestId: UpdateRequestId, + pub status: UpdateRequestStatus, + pub packageId: Package, + pub installPos: i32, + pub createdAt: String, +} + +/// The status of an `UpdateRequest` from Core. +#[derive(RustcDecodable, RustcEncodable, PartialEq, Eq, Debug, Clone)] +pub enum UpdateRequestStatus { + Pending, + InFlight, + Canceled, + Failed, + Finished +} + /// Encodes the name and version of a specific package. -#[derive(Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, Clone)] +#[derive(RustcDecodable, RustcEncodable, PartialEq, Eq, Debug, Clone)] pub struct Package { pub name: String, - pub version: Version + pub version: String } impl Display for Package { @@ -20,19 +42,6 @@ impl Display for Package { } -/// Encapsulate a `String` type as the id of a specific update request. -pub type UpdateRequestId = String; - -/// A single pending update request to be installed by the client. -#[allow(non_snake_case)] -#[derive(Clone, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable)] -pub struct PendingUpdateRequest { - pub requestId: UpdateRequestId, - pub installPos: i32, - pub packageId: Package, - pub createdAt: String -} - /// A notification from RVI that a new update is available. #[derive(RustcDecodable, RustcEncodable, PartialEq, Eq, Debug, Clone)] pub struct UpdateAvailable { @@ -59,8 +68,7 @@ pub struct ChunkReceived { pub chunks: Vec<u64>, } -/// A notification to indicate to any external package manager that the package -/// download has successfully completed. +/// A notification to an external package manager that the package was downloaded. #[derive(RustcDecodable, RustcEncodable, PartialEq, Eq, Debug, Clone)] pub struct DownloadComplete { pub update_id: String, @@ -68,12 +76,9 @@ pub struct DownloadComplete { pub signature: String } -impl Default for DownloadComplete { - fn default() -> Self { - DownloadComplete { - update_id: "".to_string(), - update_image: "".to_string(), - signature: "".to_string() - } - } +/// A notification to an external package manager that the package download failed. +#[derive(RustcDecodable, RustcEncodable, PartialEq, Eq, Debug, Clone)] +pub struct DownloadFailed { + pub update_id: String, + pub reason: String } diff --git a/src/gateway/dbus.rs b/src/gateway/dbus.rs index ea807f6..07a3b9c 100644 --- a/src/gateway/dbus.rs +++ b/src/gateway/dbus.rs @@ -47,7 +47,7 @@ impl Gateway for DBus { fn pulse(&self, event: Event) { match event { - Event::PendingUpdateAvailable(avail) => { + Event::UpdateAvailable(avail) => { let msg = self.new_message("updateAvailable", &[ MessageItem::from(avail.update_id), MessageItem::from(avail.signature), diff --git a/src/gateway/socket.rs b/src/gateway/socket.rs index 4022c86..571e34c 100644 --- a/src/gateway/socket.rs +++ b/src/gateway/socket.rs @@ -6,7 +6,7 @@ use std::net::Shutdown; use std::sync::{Arc, Mutex}; use std::{fs, thread}; -use datatype::{Command, Error, Event}; +use datatype::{Command, DownloadFailed, Error, Event}; use super::{Gateway, Interpret}; use unix_socket::{UnixListener, UnixStream}; @@ -66,7 +66,7 @@ impl Gateway for Socket { json::encode(&EventWrapper { version: "0.1".to_string(), event: "DownloadFailed".to_string(), - data: DownloadFailedWrapper { update_id: id, reason: reason } + data: DownloadFailed { update_id: id, reason: reason } }).expect("couldn't encode DownloadFailed event") } @@ -107,12 +107,6 @@ pub struct EventWrapper<E: Encodable> { pub data: E } -#[derive(RustcEncodable, RustcDecodable, PartialEq, Eq, Debug)] -pub struct DownloadFailedWrapper { - pub update_id: String, - pub reason: String -} - #[cfg(test)] mod tests { diff --git a/src/http/auth_client.rs b/src/http/auth_client.rs index f97f4d7..d9e28cb 100644 --- a/src/http/auth_client.rs +++ b/src/http/auth_client.rs @@ -197,7 +197,8 @@ impl Handler<Stream> for AuthHandler { } else if resp.status().is_redirection() { self.redirect_request(resp); Next::end() - } else if resp.status() == &StatusCode::Forbidden { + } else if resp.status() == &StatusCode::Unauthorized + || resp.status() == &StatusCode::Forbidden { self.resp_tx.send(Err(Error::Authorization(format!("{}", resp.status())))); Next::end() } else { diff --git a/src/interpreter.rs b/src/interpreter.rs index c009d3c..1b671d8 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -4,7 +4,8 @@ use std; use std::borrow::Cow; use datatype::{AccessToken, Auth, ClientId, ClientSecret, Command, Config, - Error, Event, Package, UpdateReport, UpdateRequestId, UpdateResultCode}; + Error, Event, Package, UpdateReport, UpdateRequestStatus as Status, + UpdateResultCode}; use gateway::Interpret; use http::{AuthClient, Client}; use oauth2::authenticate; @@ -51,25 +52,24 @@ impl Interpreter<Event, Command> for EventInterpreter { ctx.send(Command::Authenticate(None)); } - Event::InFlightUpdatesReceived(requests) => { - if self.package_manager != PackageManager::Off { - self.package_manager.installed_packages().map(|packages| { - for request in requests { - let id = request.requestId.clone(); - if packages.contains(&request.packageId) { - let report = UpdateReport::single(id, UpdateResultCode::OK, "".to_string()); - ctx.send(Command::SendUpdateReport(report)); + Event::UpdatesReceived(requests) => { + for request in requests { + match (request.status, &self.package_manager) { + (Status::Pending, _) => ctx.send(Command::StartDownload(request.requestId.clone())), + + (Status::InFlight, &PackageManager::Off) => (), + + (Status::InFlight, _) => { + if self.package_manager.is_installed(&request.packageId) { + ctx.send(Command::SendUpdateReport(UpdateReport::single( + request.requestId.clone(), UpdateResultCode::OK, "".to_string()))); } else { - ctx.send(Command::StartDownload(id)); + ctx.send(Command::StartDownload(request.requestId.clone())); } } - }).unwrap_or_else(|err| error!("couldn't get a list of packages: {}", err)); - } - } - Event::PendingUpdatesReceived(ids) => { - for id in ids { - ctx.send(Command::StartDownload(id)); + _ => () + } } } @@ -172,23 +172,13 @@ impl<'t> GlobalInterpreter<'t> { match cmd { Command::Authenticate(_) => etx.send(Event::Authenticated), - Command::GetInFlightUpdates => { - let updates = try!(sota.get_in_flight_updates()); + Command::GetUpdateRequests => { + let mut updates = try!(sota.get_update_requests()); if updates.is_empty() { - etx.send(Event::NoInFlightUpdates); - } else { - etx.send(Event::InFlightUpdatesReceived(updates)); - } - } - - Command::GetPendingUpdates => { - let mut updates = try!(sota.get_pending_updates()); - if updates.is_empty() { - etx.send(Event::NoPendingUpdates); + etx.send(Event::NoUpdateRequests); } else { updates.sort_by_key(|u| u.installPos); - let ids = updates.iter().map(|u| u.requestId.clone()).collect::<Vec<UpdateRequestId>>(); - etx.send(Event::PendingUpdatesReceived(ids)); + etx.send(Event::UpdatesReceived(updates)); } } @@ -272,18 +262,9 @@ impl<'t> GlobalInterpreter<'t> { etx.send(Event::Authenticated); } - Command::GetInFlightUpdates | - Command::GetPendingUpdates | - Command::ListInstalledPackages | - Command::ListSystemInfo | - Command::SendInstalledPackages(_) | - Command::SendInstalledSoftware(_) | - Command::SendUpdateReport(_) | - Command::SendSystemInfo | - Command::StartDownload(_) | - Command::StartInstall(_) => etx.send(Event::NotAuthenticated), - Command::Shutdown => std::process::exit(0), + + _ => etx.send(Event::NotAuthenticated) } Ok(()) diff --git a/src/main.rs b/src/main.rs index 3fd7797..031734c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,7 +51,7 @@ fn start_update_poller(interval: u64, itx: Sender<Interpret>) { loop { let _ = tick.recv(); itx.send(Interpret { - command: Command::GetPendingUpdates, + command: Command::GetUpdateRequests, response_tx: Some(Arc::new(Mutex::new(etx.clone()))) }); let _ = erx.recv(); diff --git a/src/package_manager/package_manager.rs b/src/package_manager/package_manager.rs index 09556a0..173a636 100644 --- a/src/package_manager/package_manager.rs +++ b/src/package_manager/package_manager.rs @@ -49,6 +49,12 @@ impl PackageManager { } } + /// Indicates whether a specific package is installed on the device. + pub fn is_installed(&self, package: &Package) -> bool { + self.installed_packages().map(|packages| packages.contains(package)) + .unwrap_or_else(|err| { error!("couldn't get a list of packages: {}", err); false }) + } + /// Returns a string representation of the package manager's extension. pub fn extension(&self) -> String { match *self { diff --git a/src/rvi/parameters.rs b/src/rvi/parameters.rs index fc5d663..e23ce9d 100644 --- a/src/rvi/parameters.rs +++ b/src/rvi/parameters.rs @@ -22,7 +22,7 @@ pub struct Notify { impl Parameter for Notify { fn handle(&self, remote: &Mutex<RemoteServices>, _: &Mutex<Transfers>) -> Result<Option<Event>, String> { remote.lock().unwrap().backend = Some(self.services.clone()); - Ok(Some(Event::PendingUpdateAvailable(self.update_available.clone()))) + Ok(Some(Event::UpdateAvailable(self.update_available.clone()))) } } diff --git a/src/sota.rs b/src/sota.rs index 5759b9f..aacf7c4 100644 --- a/src/sota.rs +++ b/src/sota.rs @@ -4,7 +4,7 @@ use std::io; use std::path::PathBuf; use datatype::{Config, DeviceReport, DownloadComplete, Error, Package, - PendingUpdateRequest, UpdateRequestId, UpdateReport, Url}; + UpdateReport, UpdateRequest, UpdateRequestId, Url}; use http::Client; @@ -36,20 +36,13 @@ impl<'c, 'h> Sota<'c, 'h> { Ok(try!(path.to_str().ok_or(Error::Parse(format!("Path is not valid UTF-8: {:?}", path)))).to_string()) } - /// Query the Core server for any pending package updates. - pub fn get_pending_updates(&mut self) -> Result<Vec<PendingUpdateRequest>, Error> { - let resp_rx = self.client.get(self.endpoint(""), None); - let resp = try!(resp_rx.recv().ok_or(Error::Client("couldn't get pending updates".to_string()))); - let text = try!(String::from_utf8(try!(resp))); - Ok(try!(json::decode::<Vec<PendingUpdateRequest>>(&text))) - } - - /// Query the Core server for any in-flight package updates. - pub fn get_in_flight_updates(&mut self) -> Result<Vec<PendingUpdateRequest>, Error> { + /// Query the Core server for any pending or in-flight package updates. + pub fn get_update_requests(&mut self) -> Result<Vec<UpdateRequest>, Error> { + let _ = self.client.get(self.endpoint(""), None); // FIXME(PRO-1352): single endpoint let resp_rx = self.client.get(self.endpoint("/queued"), None); - let resp = try!(resp_rx.recv().ok_or(Error::Client("couldn't get in-flight updates".to_string()))); + let resp = try!(resp_rx.recv().ok_or(Error::Client("couldn't get new updates".to_string()))); let text = try!(String::from_utf8(try!(resp))); - Ok(try!(json::decode::<Vec<PendingUpdateRequest>>(&text))) + Ok(try!(json::decode::<Vec<UpdateRequest>>(&text))) } /// Download a specific update from the Core server. @@ -112,29 +105,30 @@ mod tests { use rustc_serialize::json; use super::*; - use datatype::{Config, Package, PendingUpdateRequest}; + use datatype::{Config, Package, UpdateRequest, UpdateRequestStatus}; use http::TestClient; #[test] - fn test_get_pending_updates() { - let pending_update = PendingUpdateRequest { + fn test_get_update_requests() { + let pending_update = UpdateRequest { requestId: "someid".to_string(), - installPos: 0, + status: UpdateRequestStatus::Pending, packageId: Package { name: "fake-pkg".to_string(), version: "0.1.1".to_string() }, + installPos: 0, createdAt: "2010-01-01".to_string() }; let json = format!("[{}]", json::encode(&pending_update).unwrap()); let mut sota = Sota { config: &Config::default(), - client: &mut TestClient::from(vec![json.to_string()]), + client: &mut TestClient::from(vec![json.to_string(), "[]".to_string()]), }; - let updates: Vec<PendingUpdateRequest> = sota.get_pending_updates().unwrap(); + let updates: Vec<UpdateRequest> = sota.get_update_requests().unwrap(); let ids: Vec<String> = updates.iter().map(|p| p.requestId.clone()).collect(); assert_eq!(ids, vec!["someid".to_string()]) } diff --git a/tests/sota.toml b/tests/sota.toml index 87cf144..0444a71 100644 --- a/tests/sota.toml +++ b/tests/sota.toml @@ -33,8 +33,8 @@ socket = false websocket = true [network] -http_server = "http://127.0.0.1:8888" -rvi_edge_server = "http://127.0.0.1:9080" +http_server = "127.0.0.1:8888" +rvi_edge_server = "127.0.0.1:9080" socket_commands_path = "/tmp/sota-commands.socket" socket_events_path = "/tmp/sota-events.socket" websocket_server = "127.0.0.1:3012" |