Skip to content

Commit

Permalink
Merged PR 888625: HTTP Management Implementation for System Info
Browse files Browse the repository at this point in the history
Hooked up Docker + Added tests for System Info for HTTP Management.
It's working End2End.
  • Loading branch information
aribeironovaes committed Jun 11, 2018
1 parent 328b4fa commit 02df09e
Show file tree
Hide file tree
Showing 15 changed files with 401 additions and 17 deletions.
16 changes: 12 additions & 4 deletions edgelet/docker-rs/src/models/swarm_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ pub struct SwarmInfo {
/// IP address at which this node can be reached by other nodes in the swarm.
#[serde(rename = "NodeAddr", skip_serializing_if = "Option::is_none")]
node_addr: Option<String>,
//TODO: This change was due to SwarmInfo, Local Node Stat returning String, instead of STATE. So the Swagger is not matching with api.
//If Auto generate tool is run again, make sure this is working.
#[serde(rename = "LocalNodeState", skip_serializing_if = "Option::is_none")]
local_node_state: Option<::models::LocalNodeState>,
local_node_state: Option<String>,
#[serde(rename = "ControlAvailable", skip_serializing_if = "Option::is_none")]
control_available: Option<bool>,
#[serde(rename = "Error", skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -90,19 +92,25 @@ impl SwarmInfo {
self.node_addr = None;
}

pub fn set_local_node_state(&mut self, local_node_state: ::models::LocalNodeState) {
//TODO: This change was due to SwarmInfo, Local Node Stat returning String, instead of STATE. So the Swagger is not matching with api.
//If Auto generate tool is run again, make sure this is working.
pub fn set_local_node_state(&mut self, local_node_state: String) {
self.local_node_state = Some(local_node_state);
}

pub fn with_local_node_state(
mut self,
local_node_state: ::models::LocalNodeState,
//TODO: This change was due to SwarmInfo, Local Node Stat returning String, instead of STATE. So the Swagger is not matching with api.
//If Auto generate tool is run again, make sure this is working.
local_node_state: String,
) -> SwarmInfo {
self.local_node_state = Some(local_node_state);
self
}

pub fn local_node_state(&self) -> Option<&::models::LocalNodeState> {
//TODO: This change was due to SwarmInfo, Local Node Stat returning String, instead of STATE. So the Swagger is not matching with api.
//If Auto generate tool is run again, make sure this is working.
pub fn local_node_state(&self) -> Option<&String> {
self.local_node_state.as_ref()
}

Expand Down
8 changes: 7 additions & 1 deletion edgelet/edgelet-core/src/authorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ mod tests {
use futures::future;
use futures::future::FutureResult;
use futures::stream::Empty;
use module::{LogOptions, Module, ModuleRegistry, ModuleRuntimeState, ModuleSpec};
use module::{LogOptions, Module, ModuleRegistry, ModuleRuntimeState, ModuleSpec,
SystemInfo as CoreSystemInfo};

#[test]
fn should_authorize_anonymous() {
Expand Down Expand Up @@ -389,6 +390,7 @@ mod tests {
type RestartFuture = FutureResult<(), Self::Error>;
type StartFuture = FutureResult<(), Self::Error>;
type StopFuture = FutureResult<(), Self::Error>;
type SystemInfoFuture = FutureResult<CoreSystemInfo, Self::Error>;

fn init(&self) -> Self::InitFuture {
notimpl_error!()
Expand All @@ -406,6 +408,10 @@ mod tests {
notimpl_error!()
}

fn system_info(&self) -> Self::SystemInfoFuture {
notimpl_error!()
}

fn restart(&self, _id: &str) -> Self::RestartFuture {
notimpl_error!()
}
Expand Down
2 changes: 1 addition & 1 deletion edgelet/edgelet-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub use crypto::{Certificate, CreateCertificate, Decrypt, Encrypt, GetTrustBundl
pub use error::{Error, ErrorKind};
pub use identity::{AuthType, Identity, IdentityManager, IdentitySpec};
pub use module::{LogOptions, LogTail, Module, ModuleRegistry, ModuleRuntime, ModuleRuntimeState,
ModuleSpec, ModuleStatus};
ModuleSpec, ModuleStatus, SystemInfo};

pub struct Edgelet<T>
where
Expand Down
49 changes: 49 additions & 0 deletions edgelet/edgelet-core/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,31 @@ pub trait ModuleRegistry {
fn remove(&self, name: &str) -> Self::RemoveFuture;
}

#[derive(Debug)]
pub struct SystemInfo {
/// OS Type of the Host. Example of value expected: \"linux\" and \"windows\".
os_type: String,
/// Hardware architecture of the host. Example of value expected: arm32, x86, amd64
architecture: String,
}

impl SystemInfo {
pub fn new(os_type: String, architecture: String) -> Self {
SystemInfo {
os_type,
architecture,
}
}

pub fn os_type(&self) -> &str {
&self.os_type
}

pub fn architecture(&self) -> &str {
&self.architecture
}
}

pub trait ModuleRuntime {
type Error: Fail;

Expand All @@ -327,13 +352,15 @@ pub trait ModuleRuntime {
type RestartFuture: Future<Item = (), Error = Self::Error>;
type StartFuture: Future<Item = (), Error = Self::Error>;
type StopFuture: Future<Item = (), Error = Self::Error>;
type SystemInfoFuture: Future<Item = SystemInfo, Error = Self::Error>;

fn init(&self) -> Self::InitFuture;
fn create(&self, module: ModuleSpec<Self::Config>) -> Self::CreateFuture;
fn start(&self, id: &str) -> Self::StartFuture;
fn stop(&self, id: &str) -> Self::StopFuture;
fn restart(&self, id: &str) -> Self::RestartFuture;
fn remove(&self, id: &str) -> Self::RemoveFuture;
fn system_info(&self) -> Self::SystemInfoFuture;
fn list(&self) -> Self::ListFuture;
fn logs(&self, id: &str, options: &LogOptions) -> Self::LogsFuture;
fn registry(&self) -> &Self::ModuleRegistry;
Expand Down Expand Up @@ -417,4 +444,26 @@ mod tests {
},
}
}

#[test]
fn system_info_new_and_access_succeed() {
//arrange
let system_info = SystemInfo::new(
"testValueOsType".to_string(),
"testArchitectureType".to_string(),
);
let expected_value_os_type = "testValueOsType";
let expected_test_architecture_type = "testArchitectureType";

//act
let current_value_os_type = system_info.os_type();
let current_value_architecture_type = system_info.architecture();

//assert
assert_eq!(expected_value_os_type, current_value_os_type);
assert_eq!(
expected_test_architecture_type,
current_value_architecture_type
);
}
}
25 changes: 24 additions & 1 deletion edgelet/edgelet-docker/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ use docker::apis::client::APIClient;
use docker::apis::configuration::Configuration;
use docker::models::{ContainerCreateBody, NetworkConfig};

use edgelet_core::{LogOptions, ModuleRegistry, ModuleRuntime, ModuleSpec};
use edgelet_core::{LogOptions, ModuleRegistry, ModuleRuntime, ModuleSpec,
SystemInfo as CoreSystemInfo};
use edgelet_http::UrlConnector;

use error::{Error, Result};
Expand Down Expand Up @@ -150,6 +151,7 @@ impl ModuleRuntime for DockerModuleRuntime {
type RestartFuture = Box<Future<Item = (), Error = Self::Error>>;
type StartFuture = Box<Future<Item = (), Error = Self::Error>>;
type StopFuture = Box<Future<Item = (), Error = Self::Error>>;
type SystemInfoFuture = Box<Future<Item = CoreSystemInfo, Error = Self::Error>>;

fn init(&self) -> Self::InitFuture {
let created = self.network_id
Expand Down Expand Up @@ -238,6 +240,27 @@ impl ModuleRuntime for DockerModuleRuntime {
)
}

fn system_info(&self) -> Self::SystemInfoFuture {
Box::new(
self.client
.system_api()
.system_info()
.map(|system_info| {
CoreSystemInfo::new(
system_info
.os_type()
.unwrap_or(&String::from("Unknown"))
.to_string(),
system_info
.architecture()
.unwrap_or(&String::from("Unknown"))
.to_string(),
)
})
.map_err(Error::from),
)
}

fn restart(&self, id: &str) -> Self::RestartFuture {
Box::new(
self.client
Expand Down
125 changes: 125 additions & 0 deletions edgelet/edgelet-docker/tests/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -822,3 +822,128 @@ fn runtime_init_network_exist_do_not_create() {
assert_eq!(true, *list_got_called_lock_cloned.read().unwrap());
assert_eq!(false, *create_got_called_lock_cloned.read().unwrap());
}

#[test]
fn runtime_system_info_succeed() {
//arrange
let (sender, receiver) = channel();

let system_info_got_called_lock = Arc::new(RwLock::new(false));
let system_info_got_called_lock_cloned = system_info_got_called_lock.clone();

let port = get_unused_tcp_port();

thread::spawn(move || {
run_tcp_server(
"127.0.0.1",
port,
move |req: Request| {
let method = req.method();
match method {
&Method::Get => {
let mut system_info_got_called_w =
system_info_got_called_lock.write().unwrap();
*system_info_got_called_w = true;

assert_eq!(req.path(), "/info");

let response = json!(
{
"OSType": "linux",
"Architecture": "x86_64",
}
).to_string();

return Box::new(future::ok(
Response::new()
.with_header(ContentLength(response.len() as u64))
.with_header(ContentType::json())
.with_body(response)
.with_status(StatusCode::Ok),
));
}
_ => panic!("Method is not a get neither a post."),
}
},
&sender,
);
});

// wait for server to get ready
receiver.recv().unwrap();

let mut core = Core::new().unwrap();
let mri = DockerModuleRuntime::new(
&Url::parse(&format!("http://localhost:{}/", port)).unwrap(),
&core.handle(),
).unwrap();

//act
let task = mri.system_info();
let system_info = core.run(task).unwrap();

//assert
assert_eq!(true, *system_info_got_called_lock_cloned.read().unwrap());
assert_eq!("linux", system_info.os_type());
assert_eq!("x86_64", system_info.architecture());
}

#[test]
fn runtime_system_info_none_returns_unkown() {
//arrange
let (sender, receiver) = channel();

let system_info_got_called_lock = Arc::new(RwLock::new(false));
let system_info_got_called_lock_cloned = system_info_got_called_lock.clone();

let port = get_unused_tcp_port();

thread::spawn(move || {
run_tcp_server(
"127.0.0.1",
port,
move |req: Request| {
let method = req.method();
match method {
&Method::Get => {
let mut system_info_got_called_w =
system_info_got_called_lock.write().unwrap();
*system_info_got_called_w = true;

assert_eq!(req.path(), "/info");

let response = json!({}).to_string();

return Box::new(future::ok(
Response::new()
.with_header(ContentLength(response.len() as u64))
.with_header(ContentType::json())
.with_body(response)
.with_status(StatusCode::Ok),
));
}
_ => panic!("Method is not a get neither a post."),
}
},
&sender,
);
});

// wait for server to get ready
receiver.recv().unwrap();

let mut core = Core::new().unwrap();
let mri = DockerModuleRuntime::new(
&Url::parse(&format!("http://localhost:{}/", port)).unwrap(),
&core.handle(),
).unwrap();

//act
let task = mri.system_info();
let system_info = core.run(task).unwrap();

//assert
assert_eq!(true, *system_info_got_called_lock_cloned.read().unwrap());
assert_eq!("Unknown", system_info.os_type());
assert_eq!("Unknown", system_info.architecture());
}
6 changes: 6 additions & 0 deletions edgelet/edgelet-http-mgmt/src/client/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::fmt;
use std::rc::Rc;
use std::str::FromStr;

use edgelet_core::SystemInfo as CoreSystemInfo;
use edgelet_core::*;
use edgelet_docker::{self, DockerConfig};
use edgelet_http::{UrlConnector, API_VERSION};
Expand Down Expand Up @@ -153,6 +154,11 @@ impl ModuleRuntime for ModuleClient {
type RestartFuture = Box<Future<Item = (), Error = Self::Error>>;
type StartFuture = Box<Future<Item = (), Error = Self::Error>>;
type StopFuture = Box<Future<Item = (), Error = Self::Error>>;
type SystemInfoFuture = Box<Future<Item = CoreSystemInfo, Error = Self::Error>>;

fn system_info(&self) -> Self::SystemInfoFuture {
unimplemented!()
}

fn init(&self) -> Self::InitFuture {
future::ok(())
Expand Down
5 changes: 5 additions & 0 deletions edgelet/edgelet-http-mgmt/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

mod identity;
mod module;
mod system_info;

use std::io;

Expand All @@ -17,6 +18,8 @@ use serde::Serialize;

use self::identity::*;
use self::module::*;
use self::system_info::*;

use IntoResponse;

lazy_static! {
Expand Down Expand Up @@ -57,6 +60,8 @@ impl ManagementService {
post "/identities" => Authorization::new(CreateIdentity::new(identity.clone()), Policy::Module(&*AGENT_NAME), runtime.clone()),
put "/identities/(?P<name>[^/]+)" => Authorization::new(UpdateIdentity::new(identity.clone()), Policy::Module(&*AGENT_NAME), runtime.clone()),
delete "/identities/(?P<name>[^/]+)" => Authorization::new(DeleteIdentity::new(identity.clone()), Policy::Module(&*AGENT_NAME), runtime.clone()),

get "/systeminfo" => Authorization::new(GetSystemInfo::new(runtime.clone()), Policy::Anonymous, runtime.clone()),
);
let inner = router.new_service()?;
let service = ManagementService { inner };
Expand Down
2 changes: 1 addition & 1 deletion edgelet/edgelet-http-mgmt/src/server/module/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ fn spec_to_details(spec: &ModuleSpec, module_status: &ModuleStatus) -> ModuleDet
}

#[cfg(test)]
mod tests {
pub mod tests {
use edgelet_docker::{Error as DockerError, ErrorKind as DockerErrorKind};
use futures::{Future, Stream};
use http::{Response, StatusCode};
Expand Down
Loading

0 comments on commit 02df09e

Please sign in to comment.