diff --git a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/RuntimeInfoProvider.cs b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/RuntimeInfoProvider.cs index da02dfc8c74..c6fc8c75dad 100644 --- a/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/RuntimeInfoProvider.cs +++ b/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/RuntimeInfoProvider.cs @@ -41,7 +41,7 @@ internal static ModuleRuntimeInfo GetModuleRuntimeInfo(ModuleDetails moduleDe Option exitTime = (exitStatus == null || !exitStatus.ExitTime.HasValue) ? Option.None() : Option.Some(exitStatus.ExitTime.Value); Option startTime = !moduleDetails.Status.StartTime.HasValue ? Option.None() : Option.Some(moduleDetails.Status.StartTime.Value); - if (!Enum.TryParse(moduleDetails.Status.RuntimeStatus.Status, out ModuleStatus status)) + if (!Enum.TryParse(moduleDetails.Status.RuntimeStatus.Status, true, out ModuleStatus status)) { status = ModuleStatus.Unknown; } @@ -50,7 +50,7 @@ internal static ModuleRuntimeInfo GetModuleRuntimeInfo(ModuleDetails moduleDe { throw new InvalidOperationException($"Module config is of type {moduleDetails.Config.Settings.GetType()}. Expected type JObject"); } - T config = jobject.ToObject(); + var config = jobject.ToObject(); var moduleRuntimeInfo = new ModuleRuntimeInfo(moduleDetails.Name, moduleDetails.Type, status, moduleDetails.Status.RuntimeStatus.Description, exitCode, startTime, exitTime, config); diff --git a/edgelet/docker-rs/src/models/container_summary.rs b/edgelet/docker-rs/src/models/container_summary.rs index a05d1690b86..eecc0a97042 100644 --- a/edgelet/docker-rs/src/models/container_summary.rs +++ b/edgelet/docker-rs/src/models/container_summary.rs @@ -27,7 +27,7 @@ pub struct ContainerSummary { created: i64, #[serde(rename = "Ports")] ports: Vec<::models::Port>, - #[serde(rename = "SizeRw")] + #[serde(rename = "SizeRw", default = "i64::default")] size_rw: i64, #[serde(rename = "SizeRootFs")] size_root_fs: i64, diff --git a/edgelet/edgelet-core/src/module.rs b/edgelet/edgelet-core/src/module.rs index 11671ae3474..e20fcc48afb 100644 --- a/edgelet/edgelet-core/src/module.rs +++ b/edgelet/edgelet-core/src/module.rs @@ -16,13 +16,9 @@ use error::Result; #[serde(rename_all = "lowercase")] pub enum ModuleStatus { Unknown, - Created, - Paused, - Restarting, - Removing, - Dead, - Exited, Running, + Stopped, + Failed, } impl FromStr for ModuleStatus { @@ -221,13 +217,9 @@ mod tests { fn get_inputs() -> Vec<(&'static str, ModuleStatus)> { vec![ ("unknown", ModuleStatus::Unknown), - ("created", ModuleStatus::Created), - ("paused", ModuleStatus::Paused), - ("restarting", ModuleStatus::Restarting), - ("removing", ModuleStatus::Removing), - ("dead", ModuleStatus::Dead), - ("exited", ModuleStatus::Exited), ("running", ModuleStatus::Running), + ("stopped", ModuleStatus::Stopped), + ("failed", ModuleStatus::Failed), ] } diff --git a/edgelet/edgelet-docker/src/module.rs b/edgelet/edgelet-docker/src/module.rs index 8d9cea3cd4c..ec79c424c7e 100644 --- a/edgelet/edgelet-docker/src/module.rs +++ b/edgelet/edgelet-docker/src/module.rs @@ -33,6 +33,16 @@ impl DockerModule { } } +fn status_from_exit_code(exit_code: Option) -> Option { + exit_code.map(|code| { + if code == 0 { + ModuleStatus::Stopped + } else { + ModuleStatus::Failed + } + }) +} + impl Module for DockerModule { type Config = DockerConfig; type Error = Error; @@ -60,7 +70,16 @@ impl Module for DockerModule { .map(|state| { let status = state .status() - .and_then(|status| ModuleStatus::from_str(status).ok()) + .and_then(|status| match status.as_ref() { + "created" => Some(ModuleStatus::Stopped), + "paused" => Some(ModuleStatus::Stopped), + "restarting" => Some(ModuleStatus::Stopped), + "removing" => status_from_exit_code(state.exit_code().cloned()), + "dead" => status_from_exit_code(state.exit_code().cloned()), + "exited" => status_from_exit_code(state.exit_code().cloned()), + "running" => Some(ModuleStatus::Running), + _ => Some(ModuleStatus::Unknown), + }) .unwrap_or_else(|| ModuleStatus::Unknown); ModuleRuntimeState::default() .with_status(status) @@ -156,37 +175,42 @@ mod tests { ).unwrap(); } - fn verify_module_status(core: &mut Core, status: &ModuleStatus) { - let docker_module = DockerModule::new( - create_api_client( - &core, - InlineResponse200::new() - .with_state(InlineResponse200State::new().with_status(status.to_string())), - ), - "mod1", - DockerConfig::new("ubuntu", ContainerCreateBody::new(), None).unwrap(), - ).unwrap(); - - let state = core.run(docker_module.runtime_state()).unwrap(); - assert_eq!(status, state.status()); + fn get_inputs() -> Vec<(&'static str, i32, ModuleStatus)> { + vec![ + ("created", 0, ModuleStatus::Stopped), + ("paused", 0, ModuleStatus::Stopped), + ("restarting", 0, ModuleStatus::Stopped), + ("removing", 0, ModuleStatus::Stopped), + ("dead", 0, ModuleStatus::Stopped), + ("exited", 0, ModuleStatus::Stopped), + ("removing", -1, ModuleStatus::Failed), + ("dead", -2, ModuleStatus::Failed), + ("exited", -42, ModuleStatus::Failed), + ("running", 0, ModuleStatus::Running), + ] } #[test] fn module_status() { - let inputs = [ - ModuleStatus::Unknown, - ModuleStatus::Created, - ModuleStatus::Paused, - ModuleStatus::Restarting, - ModuleStatus::Removing, - ModuleStatus::Dead, - ModuleStatus::Exited, - ModuleStatus::Running, - ]; + let inputs = get_inputs(); let mut core = Core::new().unwrap(); - for status in inputs.iter() { - verify_module_status(&mut core, &status); + for &(docker_status, exit_code, ref module_status) in inputs.iter() { + let docker_module = DockerModule::new( + create_api_client( + &core, + InlineResponse200::new().with_state( + InlineResponse200State::new() + .with_status(docker_status.to_string()) + .with_exit_code(exit_code), + ), + ), + "mod1", + DockerConfig::new("ubuntu", ContainerCreateBody::new(), None).unwrap(), + ).unwrap(); + + let state = core.run(docker_module.runtime_state()).unwrap(); + assert_eq!(module_status, state.status()); } } @@ -223,6 +247,39 @@ mod tests { ); } + #[test] + fn module_runtime_state_failed_from_dead() { + let started_at = Utc::now().to_rfc3339(); + let finished_at = (Utc::now() + Duration::hours(1)).to_rfc3339(); + let mut core = Core::new().unwrap(); + let docker_module = DockerModule::new( + create_api_client( + &core, + InlineResponse200::new() + .with_state( + InlineResponse200State::new() + .with_exit_code(10) + .with_status("dead".to_string()) + .with_started_at(started_at.clone()) + .with_finished_at(finished_at.clone()), + ) + .with_id("mod1".to_string()), + ), + "mod1", + DockerConfig::new("ubuntu", ContainerCreateBody::new(), None).unwrap(), + ).unwrap(); + + let runtime_state = core.run(docker_module.runtime_state()).unwrap(); + assert_eq!(ModuleStatus::Failed, *runtime_state.status()); + assert_eq!(10, *runtime_state.exit_code().unwrap()); + assert_eq!(&"dead", &runtime_state.status_description().unwrap()); + assert_eq!(started_at, runtime_state.started_at().unwrap().to_rfc3339()); + assert_eq!( + finished_at, + runtime_state.finished_at().unwrap().to_rfc3339() + ); + } + #[test] fn module_runtime_state_with_bad_started_at() { let started_at = "not really a date".to_string(); diff --git a/edgelet/edgelet-http-mgmt/src/server/module/create.rs b/edgelet/edgelet-http-mgmt/src/server/module/create.rs index 26a9eca1ec4..81e609b4f03 100644 --- a/edgelet/edgelet-http-mgmt/src/server/module/create.rs +++ b/edgelet/edgelet-http-mgmt/src/server/module/create.rs @@ -152,7 +152,7 @@ mod tests { "microsoft/test-image", details.config().settings().get("image").unwrap() ); - assert_eq!("created", details.status().runtime_status().status()); + assert_eq!("stopped", details.status().runtime_status().status()); assert_eq!(160, b.len()); Ok(()) diff --git a/edgelet/edgelet-http-mgmt/src/server/module/mod.rs b/edgelet/edgelet-http-mgmt/src/server/module/mod.rs index c35618922fe..727ba8975b1 100644 --- a/edgelet/edgelet-http-mgmt/src/server/module/mod.rs +++ b/edgelet/edgelet-http-mgmt/src/server/module/mod.rs @@ -151,7 +151,7 @@ fn spec_to_details(spec: &ModuleSpec) -> ModuleDetails { config.set_env(e); } - let runtime_status = RuntimeStatus::new(ModuleStatus::Created.to_string()); + let runtime_status = RuntimeStatus::new(ModuleStatus::Stopped.to_string()); let status = Status::new(runtime_status); ModuleDetails::new(id, name, type_, config, status) } diff --git a/edgelet/edgelet-http-mgmt/src/server/module/update.rs b/edgelet/edgelet-http-mgmt/src/server/module/update.rs index 0f1ddff9063..8d1c23b0c92 100644 --- a/edgelet/edgelet-http-mgmt/src/server/module/update.rs +++ b/edgelet/edgelet-http-mgmt/src/server/module/update.rs @@ -152,7 +152,7 @@ mod tests { "microsoft/test-image", details.config().settings().get("image").unwrap() ); - assert_eq!("created", details.status().runtime_status().status()); + assert_eq!("stopped", details.status().runtime_status().status()); assert_eq!(160, b.len()); Ok(())