From 27f66b2127d7a975d8d16a6f28e49fcbdd299805 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 30 Aug 2022 09:43:53 +0200 Subject: [PATCH 01/21] Run CI on the project-demo branch as well --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ab48485..eddd648e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: Kudo CI on: push: - branches: [ "main" ] + branches: [ "main", "project-demo-202208" ] pull_request: - branches: [ "main" ] + branches: [ "main", "project-demo-202208" ] env: CARGO_TERM_COLOR: always From a0719010fd13705d43b113b34377e5b0ba137e82 Mon Sep 17 00:00:00 2001 From: iverly Date: Mon, 29 Aug 2022 12:11:40 +0200 Subject: [PATCH 02/21] feat(scheduler): add parser module Signed-off-by: iverly --- scheduler/src/parser/instance.rs | 87 +++++++++++++++++++++++++ scheduler/src/parser/mod.rs | 3 + scheduler/src/parser/port.rs | 82 ++++++++++++++++++++++++ scheduler/src/parser/resource.rs | 106 +++++++++++++++++++++++++++++++ 4 files changed, 278 insertions(+) create mode 100644 scheduler/src/parser/instance.rs create mode 100644 scheduler/src/parser/mod.rs create mode 100644 scheduler/src/parser/port.rs create mode 100644 scheduler/src/parser/resource.rs diff --git a/scheduler/src/parser/instance.rs b/scheduler/src/parser/instance.rs new file mode 100644 index 00000000..89d17fd0 --- /dev/null +++ b/scheduler/src/parser/instance.rs @@ -0,0 +1,87 @@ +use proto::scheduler::{Instance, Status}; + +use super::{port::PortParser, resource::ResourceParser}; + +pub struct InstanceParser {} + +impl InstanceParser { + /// It converts a `Instance` struct to a `proto::agent::Instance` struct + /// + /// Arguments: + /// + /// * `instance`: Instance - The instance to convert + pub fn to_agent_instance(instance: Instance) -> proto::agent::Instance { + proto::agent::Instance { + id: instance.id, + name: instance.name, + r#type: instance.r#type, + status: instance.status, + uri: instance.uri, + environment: instance.environnement, + resource: instance.resource.map(ResourceParser::to_agent_resource), + ports: PortParser::to_agent_ports(instance.ports), + ip: instance.ip, + } + } + + /// It converts a `proto::agent::Instance` struct to a `Instance` struct + /// + /// Arguments: + /// + /// * `instance`: proto::agent::Instance + /// + /// Returns: + /// + /// An Instance struct + pub fn from_agent_instance(instance: proto::agent::Instance) -> Instance { + Instance { + id: instance.id, + name: instance.name, + r#type: instance.r#type, + status: instance.status, + uri: instance.uri, + environnement: instance.environment, + resource: instance.resource.map(ResourceParser::from_agent_resource), + ports: PortParser::from_agent_ports(instance.ports), + ip: instance.ip, + } + } + + /// It creates a fake agent instance + /// + /// Arguments: + /// + /// * `id`: The id of the agent instance. + pub fn fake_agent_instance(id: String) -> proto::agent::Instance { + proto::agent::Instance { + id, + name: "".to_string(), + r#type: proto::agent::Type::Container.into(), + status: Status::Stopping.into(), + uri: "".to_string(), + environment: vec![], + resource: None, + ports: vec![], + ip: "".to_string(), + } + } + + /// It creates a fake controller instance + /// + /// Arguments: + /// + /// * `id`: The id of the instance + pub fn fake_controller_instance(id: String) -> proto::controller::Instance { + proto::controller::Instance { + id, + name: "".to_string(), + r#type: proto::agent::Type::Container.into(), + state: Status::Stopping.into(), + uri: "".to_string(), + environnement: vec![], + resource: None, + ports: vec![], + ip: "".to_string(), + } + } +} diff --git a/scheduler/src/parser/mod.rs b/scheduler/src/parser/mod.rs new file mode 100644 index 00000000..672d117f --- /dev/null +++ b/scheduler/src/parser/mod.rs @@ -0,0 +1,3 @@ +pub mod instance; +pub mod port; +pub mod resource; diff --git a/scheduler/src/parser/port.rs b/scheduler/src/parser/port.rs new file mode 100644 index 00000000..62bd4f8b --- /dev/null +++ b/scheduler/src/parser/port.rs @@ -0,0 +1,82 @@ +use proto::scheduler::{InstanceStatus, NodeStatus, Port}; + +use super::resource::ResourceParser; + +pub struct PortParser {} + +impl PortParser { + /// `ports` is a vector of `Port`s, and we want to convert it into a vector of `proto::agent::Port`s + /// + /// Arguments: + /// + /// * `ports`: Vec + /// + /// Returns: + /// + /// A vector of proto::agent::Port + pub fn to_agent_ports(ports: Vec) -> Vec { + ports + .into_iter() + .map(|port| proto::agent::Port { + source: port.source, + destination: port.destination, + }) + .collect() + } + + /// `from_agent_ports` takes a vector of `proto::agent::Port`s and returns a vector of `Port`s + /// + /// Arguments: + /// + /// * `ports`: Vec + /// + /// Returns: + /// + /// A vector of Port structs. + pub fn from_agent_ports(ports: Vec) -> Vec { + ports + .into_iter() + .map(|port| Port { + source: port.source, + destination: port.destination, + }) + .collect() + } +} + +pub struct StatusParser {} + +impl StatusParser { + /// It takes a `proto::agent::InstanceStatus` and returns an `InstanceStatus` + /// + /// Arguments: + /// + /// * `status`: proto::agent::InstanceStatus + /// + /// Returns: + /// + /// A new InstanceStatus struct + pub fn from_agent_instance_status(status: proto::agent::InstanceStatus) -> InstanceStatus { + InstanceStatus { + id: status.id, + status: status.status, + status_description: status.description, + resource: status.resource.map(ResourceParser::from_agent_resource), + } + } + + /// It takes a `NodeStatus` struct and returns a `proto::controller::NodeStatus` struct + /// + /// Arguments: + /// + /// * `status`: NodeStatus - this is the status of the node. + pub fn to_controller_node_status(status: NodeStatus) -> proto::controller::NodeStatus { + proto::controller::NodeStatus { + id: status.id, + state: status.status, + status_description: status.status_description, + resource: status.resource.map(ResourceParser::to_controller_resource), + instances: vec![], // todo; + } + } +} diff --git a/scheduler/src/parser/resource.rs b/scheduler/src/parser/resource.rs new file mode 100644 index 00000000..987e2fac --- /dev/null +++ b/scheduler/src/parser/resource.rs @@ -0,0 +1,106 @@ +use proto::scheduler::{Resource, ResourceSummary}; + +pub struct ResourceParser {} + +impl ResourceParser { + /// It converts a Resource struct to a proto::agent::Resource struct. + /// + /// Arguments: + /// + /// * `resource`: Resource + /// + /// Returns: + /// + /// A proto::agent::Resource struct + pub fn to_agent_resource(resource: Resource) -> proto::agent::Resource { + proto::agent::Resource { + limit: resource.limit.map(Self::to_agent_resourcesummary), + usage: resource.usage.map(Self::to_agent_resourcesummary), + } + } + + /// It converts a Resource struct to a proto::controller::Resource struct. + /// + /// Arguments: + /// + /// * `resource`: Resource + /// + /// Returns: + /// + /// A proto::controller::Resource struct + pub fn to_controller_resource(resource: Resource) -> proto::controller::Resource { + proto::controller::Resource { + limit: resource.limit.map(Self::to_controller_resourcesummary), + usage: resource.usage.map(Self::to_controller_resourcesummary), + } + } + + /// It converts a proto::agent::Resource struct to a Resource struct. + /// + /// Arguments: + /// + /// * `resource`: proto::agent::Resource + /// + /// Returns: + /// + /// A Resource struct + pub fn from_agent_resource(resource: proto::agent::Resource) -> Resource { + Resource { + limit: resource.limit.map(Self::from_agent_resourcesummary), + usage: resource.usage.map(Self::from_agent_resourcesummary), + } + } + + /// It converts a ResourceSummary to a proto::agent::ResourceSummary struct. + /// + /// Arguments: + /// + /// * `resource`: ResourceSummary + /// + /// Returns: + /// + /// A proto::agent::ResourceSummary struct + pub fn to_agent_resourcesummary(resource: ResourceSummary) -> proto::agent::ResourceSummary { + proto::agent::ResourceSummary { + cpu: resource.cpu, + memory: resource.memory, + disk: resource.disk, + } + } + + /// It converts a ResourceSummary to a proto::agent::ResourceSummary struct. + /// + /// Arguments: + /// + /// * `resource`: ResourceSummary + /// + /// Returns: + /// + /// A proto::agent::ResourceSummary struct + pub fn to_controller_resourcesummary( + resource: ResourceSummary, + ) -> proto::controller::ResourceSummary { + proto::controller::ResourceSummary { + cpu: resource.cpu, + memory: resource.memory, + disk: resource.disk, + } + } + + /// It converts a proto::agent::ResourceSummary to a ResourceSummary struct. + /// + /// Arguments: + /// + /// * `resource`: proto::agent::ResourceSummary + /// + /// Returns: + /// + /// A ResourceSummary struct + pub fn from_agent_resourcesummary(resource: proto::agent::ResourceSummary) -> ResourceSummary { + ResourceSummary { + cpu: resource.cpu, + memory: resource.memory, + disk: resource.disk, + } + } +} From fac8cd07c4968ad576d041ad67ff8d99b33fb71e Mon Sep 17 00:00:00 2001 From: iverly Date: Mon, 29 Aug 2022 12:12:15 +0200 Subject: [PATCH 03/21] feat(scheduler): add InstanceScheduled structure Signed-off-by: iverly --- .../listener.rs} | 52 +++++------- scheduler/src/instance/mod.rs | 2 + scheduler/src/instance/scheduled.rs | 82 +++++++++++++++++++ scheduler/src/lib.rs | 9 +- scheduler/src/manager.rs | 6 +- 5 files changed, 115 insertions(+), 36 deletions(-) rename scheduler/src/{instance_listener.rs => instance/listener.rs} (54%) create mode 100644 scheduler/src/instance/mod.rs create mode 100644 scheduler/src/instance/scheduled.rs diff --git a/scheduler/src/instance_listener.rs b/scheduler/src/instance/listener.rs similarity index 54% rename from scheduler/src/instance_listener.rs rename to scheduler/src/instance/listener.rs index 9db6fac3..2d4f7f39 100644 --- a/scheduler/src/instance_listener.rs +++ b/scheduler/src/instance/listener.rs @@ -1,11 +1,9 @@ -use log::debug; -use tokio::sync::mpsc; -use tokio_stream::wrappers::ReceiverStream; -use tonic::{Request, Response, Status}; - use proto::scheduler::{ instance_service_server::InstanceService, Instance, InstanceIdentifier, InstanceStatus, }; +use tokio::sync::mpsc; +use tokio_stream::wrappers::ReceiverStream; +use tonic::{Request, Response}; use crate::{manager::Manager, Event}; @@ -25,8 +23,8 @@ impl InstanceService for InstanceListener { async fn create( &self, request: Request, - ) -> Result, Status> { - debug!("received request: {:?}", request); + ) -> Result, tonic::Status> { + log::debug!("received gRPC request: {:?}", request); let (tx, rx) = Manager::create_mpsc_channel(); match self @@ -38,33 +36,22 @@ impl InstanceService for InstanceListener { return Ok(Response::new(ReceiverStream::new(rx))); } Err(_) => { - return Err(Status::internal("could not send event to manager")); + return Err(tonic::Status::internal("could not send event to manager")); } } } - type CreateStream = ReceiverStream>; + type CreateStream = ReceiverStream>; - async fn start(&self, request: Request) -> Result, Status> { - debug!("received request: {:?}", request); - let (tx, rx) = Manager::create_oneshot_channel(); - - match self - .sender - .send(Event::InstanceStart(request.into_inner().id, tx)) - .await - { - Ok(_) => { - return rx.await.unwrap(); - } - Err(_) => { - return Err(Status::internal("could not send event to manager")); - } - } + async fn start(&self, _: Request) -> Result, tonic::Status> { + Err(tonic::Status::unimplemented("not implemented")) } - async fn stop(&self, request: Request) -> Result, Status> { - debug!("received request: {:?}", request); + async fn stop( + &self, + request: Request, + ) -> Result, tonic::Status> { + log::debug!("received gRPC request: {:?}", request); let (tx, rx) = Manager::create_oneshot_channel(); match self @@ -76,13 +63,16 @@ impl InstanceService for InstanceListener { return rx.await.unwrap(); } Err(_) => { - return Err(Status::internal("could not send event to manager")); + return Err(tonic::Status::internal("could not send event to manager")); } } } - async fn destroy(&self, request: Request) -> Result, Status> { - debug!("received request: {:?}", request); + async fn destroy( + &self, + request: Request, + ) -> Result, tonic::Status> { + log::debug!("received gRPC request: {:?}", request); let (tx, rx) = Manager::create_oneshot_channel(); match self @@ -94,7 +84,7 @@ impl InstanceService for InstanceListener { return rx.await.unwrap(); } Err(_) => { - return Err(Status::internal("could not send event to manager")); + return Err(tonic::Status::internal("could not send event to manager")); } } } diff --git a/scheduler/src/instance/mod.rs b/scheduler/src/instance/mod.rs new file mode 100644 index 00000000..5301ebcb --- /dev/null +++ b/scheduler/src/instance/mod.rs @@ -0,0 +1,2 @@ +pub mod listener; +pub mod scheduled; diff --git a/scheduler/src/instance/scheduled.rs b/scheduler/src/instance/scheduled.rs new file mode 100644 index 00000000..e71ac8e6 --- /dev/null +++ b/scheduler/src/instance/scheduled.rs @@ -0,0 +1,82 @@ +use proto::scheduler::{Instance, InstanceStatus, Status}; +use tokio::sync::mpsc; + +use crate::{NodeIdentifier, ProxyError}; + +/// InstanceScheduled represents an instance that is scheduled to a node. It is used to send +/// messages to the node and contains the node identifier where it's scheduled. +/// +/// Properties: +/// +/// * `id`: The id of the instance. +/// * `instance`: The instance that is being registered. +/// * `node_id`: The node identifier of the node that the instance is running on. +/// * `tx`: This is the channel that the instance will use to send status updates to the controller. +#[derive(Debug, Clone)] +pub struct InstanceScheduled { + pub id: String, + pub instance: Instance, + pub node_id: Option, + pub tx: mpsc::Sender>, +} + +impl InstanceScheduled { + /// `new` creates a new `InstanceStatus` struct + /// + /// Arguments: + /// + /// * `id`: The id of the instance. + /// * `instance`: The instance that we want to run. + /// * `node_id`: The node identifier of the node that the instance is running on. + /// * `tx`: This is the channel that the instance will use to send status updates to the controller. + /// + /// Returns: + /// + /// A new instance of the struct `InstanceStatus` + pub fn new( + id: String, + instance: Instance, + node_id: Option, + tx: mpsc::Sender>, + ) -> Self { + Self { + id, + instance, + node_id, + tx, + } + } + + /// This function updates the status of the instance and sends the updated status to the controller + /// + /// Arguments: + /// + /// * `status`: The status of the node. + /// * `description`: A string that describes the status of the node. + /// + /// Returns: + /// + /// A Result<(), ProxyError> + pub async fn change_status( + &mut self, + status: Status, + description: Option, + ) -> Result<(), ProxyError> { + self.instance.status = status.into(); + + self.tx + .send(Ok(InstanceStatus { + id: self.id.clone(), + status: status.into(), + status_description: description.unwrap_or_else(|| "".to_string()), + resource: match self.instance.status() { + Status::Running => self.instance.resource.clone(), + _ => None, + }, + })) + .await + .map_err(|_| ProxyError::ChannelSenderError)?; + + Ok(()) + } +} diff --git a/scheduler/src/lib.rs b/scheduler/src/lib.rs index a6a4c225..524943a4 100644 --- a/scheduler/src/lib.rs +++ b/scheduler/src/lib.rs @@ -7,7 +7,7 @@ use tokio::sync::{mpsc, oneshot}; use tonic::Response; pub mod config; -pub mod instance_listener; +pub mod instance; pub mod manager; pub mod node_listener; pub mod storage; @@ -26,6 +26,12 @@ pub enum SchedulerError { Unknown, } +#[derive(Error, Debug)] +pub enum ProxyError { + #[error("an error occurred while sending a message to the channel")] + ChannelSenderError, +} + #[derive(Debug)] #[allow(dead_code)] pub struct Node { @@ -33,6 +39,7 @@ pub struct Node { } pub type NodeIdentifier = String; +pub type InstanceIdentifier = String; #[derive(Debug)] pub enum Event { diff --git a/scheduler/src/manager.rs b/scheduler/src/manager.rs index f58bd960..88cafcc9 100644 --- a/scheduler/src/manager.rs +++ b/scheduler/src/manager.rs @@ -10,11 +10,9 @@ use tokio::sync::mpsc; use tokio::{sync::oneshot, task::JoinHandle}; use tonic::{transport::Server, Response}; +use crate::instance::listener::InstanceListener; use crate::SchedulerError; -use crate::{ - config::Config, instance_listener::InstanceListener, node_listener::NodeListener, - storage::Storage, Event, Node, -}; +use crate::{config::Config, node_listener::NodeListener, storage::Storage, Event, Node}; #[derive(Debug)] pub struct Manager { From e93fc88bf86e53ae5269823d9955242f883a0437 Mon Sep 17 00:00:00 2001 From: iverly Date: Mon, 29 Aug 2022 12:02:34 +0200 Subject: [PATCH 04/21] feat(scheduler): add NodeRegistered structure Signed-off-by: iverly --- Cargo.lock | 1 + scheduler/Cargo.toml | 1 + scheduler/src/lib.rs | 14 +- scheduler/src/manager.rs | 5 +- .../{node_listener.rs => node/listener.rs} | 43 +-- scheduler/src/node/mod.rs | 11 + scheduler/src/node/registered.rs | 279 ++++++++++++++++++ 7 files changed, 331 insertions(+), 23 deletions(-) rename scheduler/src/{node_listener.rs => node/listener.rs} (62%) create mode 100644 scheduler/src/node/mod.rs create mode 100644 scheduler/src/node/registered.rs diff --git a/Cargo.lock b/Cargo.lock index a9828deb..38c15572 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1882,6 +1882,7 @@ name = "scheduler" version = "0.1.0" dependencies = [ "anyhow", + "async-stream", "confy", "env_logger 0.8.4", "log", diff --git a/scheduler/Cargo.toml b/scheduler/Cargo.toml index d00ca89e..13e1459f 100644 --- a/scheduler/Cargo.toml +++ b/scheduler/Cargo.toml @@ -17,3 +17,4 @@ serde_derive = "1.0.142" confy = "0.4.0" anyhow = "1.0.62" thiserror = "1.0.32" +async-stream = "0.3.3" diff --git a/scheduler/src/lib.rs b/scheduler/src/lib.rs index 524943a4..45a45f8d 100644 --- a/scheduler/src/lib.rs +++ b/scheduler/src/lib.rs @@ -1,3 +1,5 @@ +use std::net::IpAddr; + use proto::scheduler::{ Instance, InstanceStatus, NodeRegisterRequest, NodeRegisterResponse, NodeStatus, NodeUnregisterRequest, NodeUnregisterResponse, @@ -9,7 +11,8 @@ use tonic::Response; pub mod config; pub mod instance; pub mod manager; -pub mod node_listener; +pub mod node; +pub mod parser; pub mod storage; #[derive(Error, Debug)] @@ -28,6 +31,14 @@ pub enum SchedulerError { #[derive(Error, Debug)] pub enum ProxyError { + #[error("an transport error occurred from tonic: {0}")] + TonicTransportError(#[from] tonic::transport::Error), + #[error("an status error occurred from tonic: {0}")] + TonicStatusError(#[from] tonic::Status), + #[error("the gRPC client was not found")] + GrpcClientNotFound, + #[error("the gRPC stream was not found")] + GrpcStreamNotFound, #[error("an error occurred while sending a message to the channel")] ChannelSenderError, } @@ -64,6 +75,7 @@ pub enum Event { // Node events NodeRegister( NodeRegisterRequest, + IpAddr, oneshot::Sender, tonic::Status>>, ), NodeUnregister( diff --git a/scheduler/src/manager.rs b/scheduler/src/manager.rs index 88cafcc9..166d5d97 100644 --- a/scheduler/src/manager.rs +++ b/scheduler/src/manager.rs @@ -11,8 +11,9 @@ use tokio::{sync::oneshot, task::JoinHandle}; use tonic::{transport::Server, Response}; use crate::instance::listener::InstanceListener; +use crate::node::listener::NodeListener; use crate::SchedulerError; -use crate::{config::Config, node_listener::NodeListener, storage::Storage, Event, Node}; +use crate::{config::Config, storage::Storage, Event, Node}; #[derive(Debug)] pub struct Manager { @@ -139,7 +140,7 @@ impl Manager { info!("received instance destroy event : {:?}", id); tx.send(Ok(Response::new(()))).unwrap(); } - Event::NodeRegister(request, tx) => { + Event::NodeRegister(request, _, tx) => { info!("received node register event : {:?}", request); tx.send(Ok(Response::new(NodeRegisterResponse::default()))) .unwrap(); diff --git a/scheduler/src/node_listener.rs b/scheduler/src/node/listener.rs similarity index 62% rename from scheduler/src/node_listener.rs rename to scheduler/src/node/listener.rs index b5b505cd..68c2341d 100644 --- a/scheduler/src/node_listener.rs +++ b/scheduler/src/node/listener.rs @@ -1,15 +1,13 @@ -use log::debug; use proto::scheduler::{ node_service_server::NodeService, NodeRegisterRequest, NodeRegisterResponse, NodeStatus, NodeUnregisterRequest, NodeUnregisterResponse, }; use tokio::sync::mpsc; -use tonic::{Request, Response, Status, Streaming}; +use tonic::{Request, Response, Streaming}; use crate::{manager::Manager, Event}; #[derive(Debug)] -#[allow(dead_code)] pub struct NodeListener { sender: mpsc::Sender, } @@ -25,33 +23,34 @@ impl NodeService for NodeListener { async fn status( &self, request: Request>, - ) -> Result, Status> { + ) -> Result, tonic::Status> { + log::debug!("received gRPC request: {:?}", request); + let mut stream = request.into_inner(); - let (tx, mut rx) = Manager::create_mpsc_channel(); + // send each status to the manager loop { + let (tx, mut rx) = Manager::create_mpsc_channel(); let message = stream.message().await?; + match message { Some(node_status) => { - debug!("Node status: {:?}", node_status); self.sender .send(Event::NodeStatus(node_status, tx.clone())) .await .unwrap(); + // wait for the manager to respond if let Some(res) = rx.recv().await { match res { - Ok(()) => { - debug!("Node status updated successfully"); - } - Err(err) => { - debug!("Error updating node status: {:?}", err); - return Err(err); - } + Ok(_) => {} + Err(err) => return Err(err), } } } None => { + log::error!("Node status stream closed"); + // todo: emit node crash event (get the node id from the first status) return Ok(Response::new(())); } } @@ -61,20 +60,23 @@ impl NodeService for NodeListener { async fn register( &self, request: Request, - ) -> Result, Status> { - debug!("{:?}", request); + ) -> Result, tonic::Status> { + log::debug!("received gRPC request: {:?}", request); + let (tx, rx) = Manager::create_oneshot_channel(); + let remote_addr = request.remote_addr().unwrap().ip(); + log::debug!("Registering a new node from: {:?}", remote_addr); match self .sender - .send(Event::NodeRegister(request.into_inner(), tx)) + .send(Event::NodeRegister(request.into_inner(), remote_addr, tx)) .await { Ok(_) => { return rx.await.unwrap(); } Err(_) => { - return Err(Status::internal("could not send event to manager")); + return Err(tonic::Status::internal("could not send event to manager")); } } } @@ -82,8 +84,9 @@ impl NodeService for NodeListener { async fn unregister( &self, request: Request, - ) -> Result, Status> { - debug!("{:?}", request); + ) -> Result, tonic::Status> { + log::debug!("received gRPC request: {:?}", request); + let (tx, rx) = Manager::create_oneshot_channel(); match self @@ -95,7 +98,7 @@ impl NodeService for NodeListener { return rx.await.unwrap(); } Err(_) => { - return Err(Status::internal("could not send event to manager")); + return Err(tonic::Status::internal("could not send event to manager")); } } } diff --git a/scheduler/src/node/mod.rs b/scheduler/src/node/mod.rs new file mode 100644 index 00000000..952b18e3 --- /dev/null +++ b/scheduler/src/node/mod.rs @@ -0,0 +1,11 @@ +use proto::scheduler::{Resource, Status}; + +pub mod listener; +pub mod registered; + +#[derive(Debug, Clone)] +pub struct Node { + pub id: String, + pub status: Status, + pub resource: Option, +} diff --git a/scheduler/src/node/registered.rs b/scheduler/src/node/registered.rs new file mode 100644 index 00000000..557ba2f5 --- /dev/null +++ b/scheduler/src/node/registered.rs @@ -0,0 +1,279 @@ +use std::{net::IpAddr, sync::Arc}; + +use proto::{ + agent::{instance_service_client::InstanceServiceClient, Signal, SignalInstruction}, + controller::node_service_client::NodeServiceClient, + scheduler::{Instance, Resource, Status}, +}; +use tokio::sync::{mpsc, Mutex}; +use tonic::{Request, Streaming}; + +use crate::{ + manager::Manager, + parser::{instance::InstanceParser, resource::ResourceParser}, + storage::{IStorage, Storage}, + InstanceIdentifier, ProxyError, +}; + +use super::Node; + +/// NodeRegistered represents a node that is registered to the cluster. It is responsible for +/// keeping track of the node's status to the controller, create/stop/destroy instances on the node. +/// +/// Properties: +/// +/// * `id`: The id of the node. +/// * `node`: The node that is being registered. +/// * `address`: The address of the node. +/// * `tx`: This is the channel that the node will use to send status updates to the controller. +/// * `grpc_client`: The grpc client for the node. +/// * `instances`: The instances that are running on the node. +#[derive(Debug)] +pub struct NodeRegistered { + pub id: String, + pub node: Node, + pub address: IpAddr, + pub tx: Option>>, + pub grpc_client: Option>, + pub instances: Storage, +} + +impl NodeRegistered { + /// `new` creates a new `NodeProxied` struct + /// + /// Arguments: + /// + /// * `id`: The id of the node. + /// * `node`: The node that this proxied node is connected to. + /// * `address`: The IP address of the node. + /// + /// Returns: + /// + /// A new instance of the NodeProxied struct. + pub fn new(id: String, node: Node, address: IpAddr) -> Self { + NodeRegistered { + id, + node, + address, + tx: None, + grpc_client: None, + instances: Storage::new(), + } + } + + /// This function connects to the gRPC server and stores the client in the `grpc_client` field of + /// the `Proxy` struct + /// + /// Returns: + /// + /// A Result<(), ProxyError> + pub async fn connect(&mut self) -> Result<(), ProxyError> { + let addr = format!("http://{}:{}", self.address, "50053"); + + let client = InstanceServiceClient::connect(addr) + .await + .map_err(ProxyError::TonicTransportError)?; + + self.grpc_client = Some(client); + Ok(()) + } + + /// It creates the node status stream between the controller and the scheduler. + /// It forwards the node status to the controller. + /// + /// Arguments: + /// + /// * `client`: The client that will be used to send the request to the node. + /// + /// Returns: + /// + /// A Result<(), ProxyError> + pub async fn open_node_status_stream( + &mut self, + client: Arc>>>, + ) -> Result<(), ProxyError> { + if self.tx.is_some() { + return Ok(()); + } + + let (tx, mut rx) = Manager::create_mpsc_channel(); + self.tx = Some(tx); + + let node_status_stream = async_stream::stream! { + loop { + let event = rx.recv().await; + match event { + Some(Ok(node_status)) => { + yield node_status; + } + Some(Err(_)) => { + break; + } + None => { + break; + } + } + } + + log::debug!("Node status stream closed"); + // todo: emit node crash event (get the node id from the first status) + }; + + let request = Self::wrap_request(node_status_stream); + + tokio::spawn(async move { + client + .lock() + .await + .as_mut() + .unwrap() + .update_node_status(request) + .await + }); + + Ok(()) + } + + /// This function updates the status of the node and sends the updated status to the controller + /// + /// Arguments: + /// + /// * `status`: The status of the node. + /// * `description`: A string that describes the status of the node. + /// * `resource`: The resource that the node is currently running. + /// + /// Returns: + /// + /// A Result<(), ProxyError> + pub async fn update_status( + &mut self, + status: Status, + description: Option, + resource: Option, + ) -> Result<(), ProxyError> { + self.node.status = status; + self.node.resource = resource; + + self.tx + .as_mut() + .ok_or(ProxyError::GrpcStreamNotFound)? + .send(Ok(proto::controller::NodeStatus { + id: self.id.clone(), + state: self.node.status.into(), + status_description: description.unwrap_or_else(|| "".to_string()), + resource: match self.node.status { + Status::Running => Some(ResourceParser::to_controller_resource( + self.node.resource.clone().unwrap(), + )), + _ => None, + }, + instances: self + .instances + .get_all() + .values() + .map(|instance| InstanceParser::fake_controller_instance(instance.id.clone())) + .collect(), + })) + .await + .map_err(|_| ProxyError::ChannelSenderError)?; + + Ok(()) + } + + /// Create a new instance to the node and return the InstanceStatus streaming. + /// + /// Arguments: + /// + /// * `instance`: Instance - The instance to create. + /// + /// Returns: + /// + /// Streaming of InstanceStatus - The streaming of the instance status. + pub async fn create_instance( + &mut self, + instance: Instance, + ) -> Result, ProxyError> { + let client = self + .grpc_client + .as_mut() + .ok_or(ProxyError::GrpcClientNotFound)?; + + let request = Self::wrap_request(InstanceParser::to_agent_instance(instance.clone())); + + let response = client + .create(request) + .await + .map_err(ProxyError::TonicStatusError)?; + + Ok(response.into_inner()) + } + + /// Send a stop signal to the node for the given instance. + /// + /// Arguments: + /// + /// * `id`: InstanceIdentifier - The instance identifier of the instance to stop. + /// + /// Returns: + /// + /// A future that resolves to a result of either a unit or a ProxyError. + pub async fn stop_instance(&mut self, id: InstanceIdentifier) -> Result<(), ProxyError> { + let client = self + .grpc_client + .as_mut() + .ok_or(ProxyError::GrpcClientNotFound)?; + + let request = Self::wrap_request(SignalInstruction { + signal: Signal::Stop.into(), + instance: Some(InstanceParser::fake_agent_instance(id)), + }); + + client + .signal(request) + .await + .map_err(ProxyError::TonicStatusError)?; + + Ok(()) + } + + /// Send a kill signal to the node for the given instance. + /// + /// Arguments: + /// + /// * `id`: InstanceIdentifier - The instance identifier of the instance to be killed. + /// + /// Returns: + /// + /// A future that resolves to a result of either a unit or a ProxyError. + pub async fn kill_instance(&mut self, id: InstanceIdentifier) -> Result<(), ProxyError> { + let client = self + .grpc_client + .as_mut() + .ok_or(ProxyError::GrpcClientNotFound)?; + + let request = Self::wrap_request(SignalInstruction { + signal: Signal::Kill.into(), + instance: Some(InstanceParser::fake_agent_instance(id)), + }); + + client + .signal(request) + .await + .map_err(ProxyError::TonicStatusError)?; + + Ok(()) + } + + /// This function takes a request and returns a request wrapped with tonic. + /// + /// Arguments: + /// + /// * `request`: The request object that you want to wrap. + /// + /// Returns: + /// + /// A Request object with the request as the inner value. + pub fn wrap_request(request: T) -> Request { + Request::new(request) + } +} From ea216d972ff88b9b03c9f371906a60a86b52aea8 Mon Sep 17 00:00:00 2001 From: Nils Ponsard Date: Mon, 29 Aug 2022 18:06:07 +0200 Subject: [PATCH 05/21] feat(kudoctl): update instance api call according to doc Signed-off-by: Nils Ponsard --- kudoctl/src/client/instance.rs | 39 ++++++++++++++++------ kudoctl/src/subcommands/apply.rs | 3 +- kudoctl/src/subcommands/delete/instance.rs | 2 +- kudoctl/src/subcommands/get/instance.rs | 2 +- kudoctl/src/subcommands/get/instances.rs | 2 +- 5 files changed, 34 insertions(+), 14 deletions(-) diff --git a/kudoctl/src/client/instance.rs b/kudoctl/src/client/instance.rs index 8b3be8cd..ee8e83e2 100644 --- a/kudoctl/src/client/instance.rs +++ b/kudoctl/src/client/instance.rs @@ -7,15 +7,22 @@ use crate::{client::types::IdResponse, resource::workload}; use super::request::Client; +#[derive(Debug, Serialize)] +struct CreateRequestBody { + pub workload_name: String, +} + /// Starts an instance on the cluster. /// /// Returns the id of the instance. -pub async fn create(client: &Client, workload_id: &String) -> anyhow::Result { +pub async fn create(client: &Client, namespace: &str, workload_id: &str) -> anyhow::Result { let response: IdResponse = (*client) - .send_json_request::( - &format!("/instance/?workloadId={}", workload_id), + .send_json_request( + &format!("/instance/{}", namespace), Method::PUT, - None, + Some(&CreateRequestBody { + workload_name: workload_id.to_owned(), + }), ) .await .context("Error creating instance")?; @@ -46,9 +53,13 @@ pub struct GetInstancesResponse { } /// List the instances in the cluster. -pub async fn list(client: &Client) -> anyhow::Result { +pub async fn list(client: &Client, namespace: &str) -> anyhow::Result { let response: GetInstancesResponse = (*client) - .send_json_request::("/instance", Method::GET, None) + .send_json_request::( + format!("/instance/{}", namespace).as_str(), + Method::GET, + None, + ) .await .context("Error getting instances")?; debug!( @@ -60,9 +71,13 @@ pub async fn list(client: &Client) -> anyhow::Result { } /// Get info about one instance. -pub async fn get(client: &Client, instance_id: &str) -> anyhow::Result { +pub async fn get(client: &Client, namespace: &str, instance_id: &str) -> anyhow::Result { let response: Instance = (*client) - .send_json_request::(&format!("/instance/{}", instance_id), Method::GET, None) + .send_json_request::( + &format!("/instance/{}/{}", namespace, instance_id), + Method::GET, + None, + ) .await .context("Error getting instance")?; debug!("Instance {} received", response.id); @@ -70,9 +85,13 @@ pub async fn get(client: &Client, instance_id: &str) -> anyhow::Result } /// Delete an instance with the given id. -pub async fn delete(client: &Client, instance_id: &str) -> anyhow::Result<()> { +pub async fn delete(client: &Client, namespace: &str, instance_id: &str) -> anyhow::Result<()> { (*client) - .send_json_request::<(), ()>(&format!("/instance/{}", instance_id), Method::DELETE, None) + .send_json_request::<(), ()>( + &format!("/instance/{}/{}", namespace, instance_id), + Method::DELETE, + None, + ) .await .context("Error deleting instance")?; debug!("Instance {} deleted", instance_id); diff --git a/kudoctl/src/subcommands/apply.rs b/kudoctl/src/subcommands/apply.rs index 55ee67dd..a1cd4f49 100644 --- a/kudoctl/src/subcommands/apply.rs +++ b/kudoctl/src/subcommands/apply.rs @@ -93,7 +93,8 @@ pub async fn execute(args: Apply, conf: &config::Config) -> Result { }; if needs_instance_create { - let instance_id = client::instance::create(&client, &workload_id).await?; + let instance_id = + client::instance::create(&client, &conf.namespace, &workload.name).await?; info!( "Workload {} created with id {} and started with instance {}", diff --git a/kudoctl/src/subcommands/delete/instance.rs b/kudoctl/src/subcommands/delete/instance.rs index 488ff21b..cdcb0b81 100644 --- a/kudoctl/src/subcommands/delete/instance.rs +++ b/kudoctl/src/subcommands/delete/instance.rs @@ -7,5 +7,5 @@ use anyhow::{Context, Result}; /// Request deletion of a resource on the cluster. pub async fn execute(conf: &config::Config, id: &str) -> Result<()> { let client = Client::new(conf).context("Error creating client")?; - client::instance::delete(&client, id).await + client::instance::delete(&client, &conf.namespace, id).await } diff --git a/kudoctl/src/subcommands/get/instance.rs b/kudoctl/src/subcommands/get/instance.rs index 02420fd1..f776585f 100644 --- a/kudoctl/src/subcommands/get/instance.rs +++ b/kudoctl/src/subcommands/get/instance.rs @@ -19,7 +19,7 @@ pub async fn execute( let search = search.unwrap(); let client = Client::new(conf).context("Error creating client")?; - let result = client::instance::get(&client, search.as_str()).await?; + let result = client::instance::get(&client, &conf.namespace, search.as_str()).await?; output::format_output(result, format) } diff --git a/kudoctl/src/subcommands/get/instances.rs b/kudoctl/src/subcommands/get/instances.rs index 75970afb..ebd2af04 100644 --- a/kudoctl/src/subcommands/get/instances.rs +++ b/kudoctl/src/subcommands/get/instances.rs @@ -14,7 +14,7 @@ pub async fn execute( show_header: bool, ) -> Result { let client = Client::new(conf).context("Error creating client")?; - let mut result = client::instance::list(&client).await?; + let mut result = client::instance::list(&client, &conf.namespace).await?; result.show_header = show_header; output::format_output(result, format) } From f46b514b0c3d9dae5eda42bea557d18e46767433 Mon Sep 17 00:00:00 2001 From: Nils Ponsard Date: Mon, 29 Aug 2022 18:53:53 +0200 Subject: [PATCH 06/21] fix(kudoctl): remove default case in match to remove warnings Some devs were annoyed about this. Signed-off-by: Nils Ponsard --- kudoctl/src/resource/parse.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/kudoctl/src/resource/parse.rs b/kudoctl/src/resource/parse.rs index 40bfc5f7..bf8c511d 100644 --- a/kudoctl/src/resource/parse.rs +++ b/kudoctl/src/resource/parse.rs @@ -32,7 +32,6 @@ resources: assert_eq!(workload.resources.memory, 2); assert_eq!(workload.resources.disk, 3); } - _ => panic!("Unexpected resource type"), } } @@ -58,7 +57,6 @@ ports: assert_eq!(workload.ports.as_ref().unwrap()[0], "8080:8080"); assert_eq!(workload.ports.as_ref().unwrap()[1], "8081:8081"); } - _ => panic!("Unexpected resource type"), } } @@ -84,7 +82,6 @@ env: assert_eq!(workload.env.as_ref().unwrap()[0], "KEY1=VALUE1"); assert_eq!(workload.env.as_ref().unwrap()[1], "KEY2=VALUE2"); } - _ => panic!("Unexpected resource type"), } } From db8946bf97a81b232a154e1903cab93f89f0559d Mon Sep 17 00:00:00 2001 From: Nils Ponsard Date: Mon, 29 Aug 2022 18:57:36 +0200 Subject: [PATCH 07/21] feat(kudoctl): delete namespace command Signed-off-by: Nils Ponsard --- kudoctl/src/client/namespace.rs | 10 ++++++++++ kudoctl/src/subcommands/delete/mod.rs | 5 +++++ kudoctl/src/subcommands/delete/namespace.rs | 11 +++++++++++ 3 files changed, 26 insertions(+) create mode 100644 kudoctl/src/subcommands/delete/namespace.rs diff --git a/kudoctl/src/client/namespace.rs b/kudoctl/src/client/namespace.rs index 14bc158b..11d82260 100644 --- a/kudoctl/src/client/namespace.rs +++ b/kudoctl/src/client/namespace.rs @@ -32,3 +32,13 @@ pub async fn list(client: &Client) -> Result { ); Ok(response) } + +/// Delete an namespace with the given id. +pub async fn delete(client: &Client, name: &str) -> anyhow::Result<()> { + (*client) + .send_json_request::<(), ()>(&format!("/namespace/{}", name), Method::DELETE, None) + .await + .context("Error deleting namespace")?; + debug!("Namespace {} deleted", name); + Ok(()) +} diff --git a/kudoctl/src/subcommands/delete/mod.rs b/kudoctl/src/subcommands/delete/mod.rs index 3a871bcd..445821d1 100644 --- a/kudoctl/src/subcommands/delete/mod.rs +++ b/kudoctl/src/subcommands/delete/mod.rs @@ -2,6 +2,7 @@ use crate::config; use anyhow::Result; use clap::{Args, ValueEnum}; mod instance; +mod namespace; mod resource; #[derive(Debug, Args)] @@ -23,6 +24,9 @@ enum Subjects { /// instances Instance, + + /// namespaces + Namespace, } /// match the subcommand to get the correct info @@ -30,6 +34,7 @@ pub async fn execute(args: Subcommand, conf: &config::Config) -> Result match args.subject { Subjects::Resource => resource::execute(conf, args.id.as_str()).await, Subjects::Instance => instance::execute(conf, args.id.as_str()).await, + Subjects::Namespace => namespace::execute(conf, args.id.as_str()).await, }?; Ok(String::new()) diff --git a/kudoctl/src/subcommands/delete/namespace.rs b/kudoctl/src/subcommands/delete/namespace.rs new file mode 100644 index 00000000..27ab121a --- /dev/null +++ b/kudoctl/src/subcommands/delete/namespace.rs @@ -0,0 +1,11 @@ +use crate::{ + client::{self, request::Client}, + config, +}; +use anyhow::{Context, Result}; + +/// Request deletion of a resource on the cluster. +pub async fn execute(conf: &config::Config, id: &str) -> Result<()> { + let client = Client::new(conf).context("Error creating client")?; + client::namespace::delete(&client, id).await +} From 75ba7fdf60473b4c56d2319a518e0a5a017da4cd Mon Sep 17 00:00:00 2001 From: Nils Ponsard Date: Mon, 29 Aug 2022 19:03:55 +0200 Subject: [PATCH 08/21] fix(kudoctl): use "default" namespace if the option is not defined Signed-off-by: Nils Ponsard --- kudoctl/src/main.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kudoctl/src/main.rs b/kudoctl/src/main.rs index acdd77ad..ca5d7a1a 100644 --- a/kudoctl/src/main.rs +++ b/kudoctl/src/main.rs @@ -56,10 +56,7 @@ async fn main() -> Result<(), Box> { global_config.controller_url = host.to_string(); } - // Set namespace if defined - if let Some(namespace) = cli.namespace { - global_config.namespace = namespace; - } + global_config.namespace = cli.namespace.unwrap_or_else(|| "default".to_string()); subcommands::match_subcommand(cli.command, &global_config).await; From cb14914cfd9a392500bfabd5ec9b9b97f60963fb Mon Sep 17 00:00:00 2001 From: Maxime Date: Mon, 29 Aug 2022 21:44:10 +0200 Subject: [PATCH 09/21] Fix : Transform filter service into a generic service We need to re-use the service into namespace & instance. To avoid code repetition, the filter service has been made generic Changes have also been made in workload to use the service without it being linked to workload Signed-off-by: Maxime --- .../lib/src/external_api/generic/filter.rs | 59 +++++++++++++++++++ .../lib/src/external_api/generic/mod.rs | 2 + .../lib/src/external_api/generic/model.rs | 10 ++++ controller/lib/src/external_api/mod.rs | 1 + .../src/external_api/workload/controller.rs | 3 +- .../lib/src/external_api/workload/filter.rs | 54 ----------------- .../lib/src/external_api/workload/mod.rs | 1 - .../lib/src/external_api/workload/model.rs | 7 --- .../lib/src/external_api/workload/service.rs | 2 +- 9 files changed, 75 insertions(+), 64 deletions(-) create mode 100644 controller/lib/src/external_api/generic/filter.rs create mode 100644 controller/lib/src/external_api/generic/mod.rs create mode 100644 controller/lib/src/external_api/generic/model.rs delete mode 100644 controller/lib/src/external_api/workload/filter.rs diff --git a/controller/lib/src/external_api/generic/filter.rs b/controller/lib/src/external_api/generic/filter.rs new file mode 100644 index 00000000..fd0b5b93 --- /dev/null +++ b/controller/lib/src/external_api/generic/filter.rs @@ -0,0 +1,59 @@ +use super::model::FilterError; +/// `FilterService` is a struct that can be used as a services to filter result. +pub struct FilterService {} + +impl Default for FilterService { + fn default() -> Self { + Self::new() + } +} + +impl FilterService { + pub fn new() -> Self { + FilterService {} + } + + /// It takes a vector of and a limit, and returns a vector that is limited to the + /// number specified by the limit + /// + /// # Arguments: + /// + /// * `vector`: A vector. + /// * `limit`: The number of elements in the vector to return. + /// + /// # Returns: + /// + /// A vector. + + pub fn limit(&mut self, vector: &Vec, mut limit: u32) -> Vec { + if limit > vector.len() as u32 { + limit = vector.len() as u32; + } + vector[0..limit as usize].to_vec() + } + + /// "Return a subset of the vector, starting at the offset index." + /// + /// The first thing we do is check if the offset is greater than the length of the vector. If + /// it is, we return an error + /// + /// # Arguments: + /// + /// * `vector`: A vector. + /// * `offset`: The offset to start from. + /// + /// # Returns: + /// + /// A vector. + + pub fn offset( + &mut self, + vector: &Vec, + offset: u32, + ) -> Result, FilterError> { + if offset > vector.len() as u32 { + return Err(FilterError::OutOfRange); + } + Ok(vector[offset as usize..].to_vec()) + } +} diff --git a/controller/lib/src/external_api/generic/mod.rs b/controller/lib/src/external_api/generic/mod.rs new file mode 100644 index 00000000..c5bc6204 --- /dev/null +++ b/controller/lib/src/external_api/generic/mod.rs @@ -0,0 +1,2 @@ +pub mod filter; +pub mod model; diff --git a/controller/lib/src/external_api/generic/model.rs b/controller/lib/src/external_api/generic/model.rs new file mode 100644 index 00000000..27fbc575 --- /dev/null +++ b/controller/lib/src/external_api/generic/model.rs @@ -0,0 +1,10 @@ +use serde::{Deserialize, Serialize}; +pub enum FilterError { + OutOfRange, +} + +#[derive(Deserialize, Serialize)] +pub struct Pagination { + pub limit: u32, + pub offset: u32, +} diff --git a/controller/lib/src/external_api/mod.rs b/controller/lib/src/external_api/mod.rs index 9859aa55..4a6b9205 100644 --- a/controller/lib/src/external_api/mod.rs +++ b/controller/lib/src/external_api/mod.rs @@ -1,2 +1,3 @@ +pub mod generic; pub mod interface; mod workload; diff --git a/controller/lib/src/external_api/workload/controller.rs b/controller/lib/src/external_api/workload/controller.rs index d94f4bc2..db09b0d7 100644 --- a/controller/lib/src/external_api/workload/controller.rs +++ b/controller/lib/src/external_api/workload/controller.rs @@ -1,7 +1,8 @@ use crate::external_api::interface::ActixAppState; +use super::model::WorkloadDTO; use super::service::WorkloadService; -use super::{model::Pagination, model::WorkloadDTO}; +use crate::external_api::generic::model::Pagination; use actix_web::http::StatusCode; use actix_web::{web, HttpResponse, Responder, Scope}; pub struct WorkloadController {} diff --git a/controller/lib/src/external_api/workload/filter.rs b/controller/lib/src/external_api/workload/filter.rs deleted file mode 100644 index 29ef2ba8..00000000 --- a/controller/lib/src/external_api/workload/filter.rs +++ /dev/null @@ -1,54 +0,0 @@ -use super::model::{Workload, WorkloadError}; - -/// `FilterService` is a struct that can be used as a service in the WorkloadService. -pub struct FilterService {} - -impl FilterService { - pub fn new() -> Self { - FilterService {} - } - - /// It takes a vector of workloads and a limit, and returns a vector of workloads that is limited to the - /// number of workloads specified by the limit - /// - /// # Arguments: - /// - /// * `workloads`: A vector of workloads to be limited. - /// * `limit`: The number of workloads to return. - /// - /// # Returns: - /// - /// A vector of workloads. - - pub fn limit(&mut self, workloads: &Vec, mut limit: u32) -> Vec { - if limit > workloads.len() as u32 { - limit = workloads.len() as u32; - } - workloads[0..limit as usize].to_vec() - } - - /// "Return a subset of the workloads vector, starting at the offset index." - /// - /// The first thing we do is check if the offset is greater than the length of the workloads vector. If - /// it is, we return an error - /// - /// # Arguments: - /// - /// * `workloads`: A vector of workloads to be filtered. - /// * `offset`: The offset to start from. - /// - /// # Returns: - /// - /// A vector of workloads - - pub fn offset( - &mut self, - workloads: &Vec, - offset: u32, - ) -> Result, WorkloadError> { - if offset > workloads.len() as u32 { - return Err(WorkloadError::OutOfRange); - } - Ok(workloads[offset as usize..].to_vec()) - } -} diff --git a/controller/lib/src/external_api/workload/mod.rs b/controller/lib/src/external_api/workload/mod.rs index 2426b69e..9c5ae537 100644 --- a/controller/lib/src/external_api/workload/mod.rs +++ b/controller/lib/src/external_api/workload/mod.rs @@ -1,4 +1,3 @@ pub mod controller; -pub mod filter; pub mod model; pub mod service; diff --git a/controller/lib/src/external_api/workload/model.rs b/controller/lib/src/external_api/workload/model.rs index a7b69c3b..69438abe 100644 --- a/controller/lib/src/external_api/workload/model.rs +++ b/controller/lib/src/external_api/workload/model.rs @@ -5,7 +5,6 @@ pub enum WorkloadError { WorkloadNotFound, Etcd(String), NameAlreadyExists(String), - OutOfRange, JsonToWorkload(String), WorkloadToJson(String), } @@ -20,7 +19,6 @@ impl WorkloadError { WorkloadError::NameAlreadyExists(name) => { HttpResponse::Conflict().body(format!("Workload with name {} already exists", name)) } - WorkloadError::OutOfRange => HttpResponse::BadRequest().body("Out of range"), WorkloadError::JsonToWorkload(err) => HttpResponse::InternalServerError().body( format!("Error while converting JSON string to workload : {}", err), ), @@ -30,11 +28,6 @@ impl WorkloadError { } } } -#[derive(Deserialize, Serialize)] -pub struct Pagination { - pub limit: u32, - pub offset: u32, -} #[derive(Deserialize, Serialize, Clone, Debug)] pub enum Type { Container = 0, diff --git a/controller/lib/src/external_api/workload/service.rs b/controller/lib/src/external_api/workload/service.rs index d74c246d..499d77af 100644 --- a/controller/lib/src/external_api/workload/service.rs +++ b/controller/lib/src/external_api/workload/service.rs @@ -1,8 +1,8 @@ use std::net::SocketAddr; -use super::filter::FilterService; use super::model::{Ressources, Type, Workload, WorkloadDTO, WorkloadError, WorkloadVector}; use crate::etcd::EtcdClient; +use crate::external_api::generic::filter::FilterService; use serde_json; /// `WorkloadService` is a struct that inpired from Controllers Provider Modules architectures. It can be used as a service in the WorkloadController .A service can use other services. From 4ecaf31fb638a9efc2c4bd837dce5414ff2afe0b Mon Sep 17 00:00:00 2001 From: Maxime Date: Tue, 30 Aug 2022 11:12:05 +0200 Subject: [PATCH 10/21] Fix : Add ressources during workload PATCH & PUT Signed-off-by: Maxime --- controller/lib/src/external_api/workload/model.rs | 1 + controller/lib/src/external_api/workload/service.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/controller/lib/src/external_api/workload/model.rs b/controller/lib/src/external_api/workload/model.rs index 69438abe..eb376bf8 100644 --- a/controller/lib/src/external_api/workload/model.rs +++ b/controller/lib/src/external_api/workload/model.rs @@ -71,6 +71,7 @@ pub struct WorkloadDTO { pub environment: Vec, pub ports: Vec, pub uri: String, + pub resources: Ressources, } #[derive(Deserialize, Serialize)] pub struct WorkloadVector { diff --git a/controller/lib/src/external_api/workload/service.rs b/controller/lib/src/external_api/workload/service.rs index 499d77af..9cb98aca 100644 --- a/controller/lib/src/external_api/workload/service.rs +++ b/controller/lib/src/external_api/workload/service.rs @@ -115,9 +115,9 @@ impl WorkloadService { uri: workload_dto.uri, environment: workload_dto.environment, resources: Ressources { - cpu: 0, - memory: 0, - disk: 0, + cpu: workload_dto.resources.cpu, + memory: workload_dto.resources.memory, + disk: workload_dto.resources.disk, }, ports: workload_dto.ports, namespace: namespace.to_string(), @@ -162,9 +162,9 @@ impl WorkloadService { uri: workload_dto.uri, environment: workload_dto.environment.to_vec(), resources: Ressources { - cpu: 0, - memory: 0, - disk: 0, + cpu: workload_dto.resources.cpu, + memory: workload_dto.resources.memory, + disk: workload_dto.resources.disk, }, ports: workload_dto.ports.to_vec(), namespace: namespace.to_string(), From f2598abcfdd3f942c9401d562f8e85675627ed28 Mon Sep 17 00:00:00 2001 From: WoodenMaiden Date: Sat, 30 Jul 2022 14:01:44 +0200 Subject: [PATCH 11/21] feat: sending instance status Signed-off-by: WoodenMaiden --- node-agent/Cargo.lock | 1252 +++++++++++++++++ node-agent/workload_manager/Cargo.lock | 1243 +++++++++++++++- .../src/workload_manager/mod.rs | 1 + .../workload_listener/containe_listener.rs | 0 .../workload_listener/container_listener.rs | 429 ++++++ .../workload_listener/error.rs | 27 + .../workload_manager/workload_listener/mod.rs | 7 + .../workload_listener/vm_listener.rs | 1 + .../workload_listener/workload_listener.rs | 13 + 9 files changed, 2972 insertions(+), 1 deletion(-) delete mode 100644 node-agent/workload_manager/src/workload_manager/workload_listener/containe_listener.rs create mode 100644 node-agent/workload_manager/src/workload_manager/workload_listener/container_listener.rs create mode 100644 node-agent/workload_manager/src/workload_manager/workload_listener/error.rs create mode 100644 node-agent/workload_manager/src/workload_manager/workload_listener/workload_listener.rs diff --git a/node-agent/Cargo.lock b/node-agent/Cargo.lock index 216c1383..7ac90331 100644 --- a/node-agent/Cargo.lock +++ b/node-agent/Cargo.lock @@ -5,3 +5,1255 @@ version = 3 [[package]] name = "agent" version = "0.1.0" + +[[package]] +name = "anyhow" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" + +[[package]] +name = "async-stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "axum" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9496f0c1d1afb7a2af4338bbe1d969cddfead41d87a9fb3aaa6d0bbc7af648" +dependencies = [ + "async-trait", + "axum-core", + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde", + "sync_wrapper", + "tokio", + "tower", + "tower-http", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4f44a0e6200e9d11a1cdc989e4b358f6e3d354fbf48478f345a17f4e43f8635" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bollard" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d4b9e55620571c2200f4be87db2a9a69e2a107fc7d206a6accad58c3536cb" +dependencies = [ + "base64", + "bollard-stubs", + "bytes", + "chrono", + "futures-core", + "futures-util", + "hex", + "http", + "hyper", + "hyperlocal", + "log", + "pin-project-lite", + "serde", + "serde_derive", + "serde_json", + "serde_urlencoded", + "thiserror", + "tokio", + "tokio-util", + "url", + "winapi", +] + +[[package]] +name = "bollard-stubs" +version = "1.42.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4295240332c78d04291f3ac857a281d5534a8e036f3dfcdaa294b22c0d424427" +dependencies = [ + "chrono", + "serde", + "serde_with", +] + +[[package]] +name = "bytes" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0b3de4a0c5e67e16066a0715723abd91edc2f9001d09c46e1dca929351e130e" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "serde", + "time", + "winapi", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-executor" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" + +[[package]] +name = "futures-macro" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "h2" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" + +[[package]] +name = "httparse" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "hyperlocal" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c" +dependencies = [ + "futures-util", + "hex", + "hyper", + "pin-project", + "tokio", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "matchit" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "node-agent" +version = "0.1.0" +dependencies = [ + "workload_manager_lib", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro2" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71adf41db68aa0daaefc69bb30bcd68ded9b9abaad5d1fbb6304c4fb390e083e" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b670f45da57fb8542ebdbb6105a925fe571b67f9e7ed9f47a06a84e72b4e7cc" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ryu" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" + +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tonic" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9d60db39854b30b835107500cf0aca0b0d14d6e1c3de124217c23a29c2ddb" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "prost-derive", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tower-layer", + "tower-service", + "tracing", + "tracing-futures", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" + +[[package]] +name = "unicode-normalization" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "workload_manager_lib" +version = "0.1.0" +dependencies = [ + "bollard", + "futures", + "tokio", + "tonic", +] diff --git a/node-agent/workload_manager/Cargo.lock b/node-agent/workload_manager/Cargo.lock index b5c768f1..a3e9d0a6 100644 --- a/node-agent/workload_manager/Cargo.lock +++ b/node-agent/workload_manager/Cargo.lock @@ -3,5 +3,1246 @@ version = 3 [[package]] -name = "workload-manager-lib" +name = "anyhow" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" + +[[package]] +name = "async-stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "axum" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9496f0c1d1afb7a2af4338bbe1d969cddfead41d87a9fb3aaa6d0bbc7af648" +dependencies = [ + "async-trait", + "axum-core", + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde", + "sync_wrapper", + "tokio", + "tower", + "tower-http", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4f44a0e6200e9d11a1cdc989e4b358f6e3d354fbf48478f345a17f4e43f8635" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bollard" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d4b9e55620571c2200f4be87db2a9a69e2a107fc7d206a6accad58c3536cb" +dependencies = [ + "base64", + "bollard-stubs", + "bytes", + "chrono", + "futures-core", + "futures-util", + "hex", + "http", + "hyper", + "hyperlocal", + "log", + "pin-project-lite", + "serde", + "serde_derive", + "serde_json", + "serde_urlencoded", + "thiserror", + "tokio", + "tokio-util", + "url", + "winapi", +] + +[[package]] +name = "bollard-stubs" +version = "1.42.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4295240332c78d04291f3ac857a281d5534a8e036f3dfcdaa294b22c0d424427" +dependencies = [ + "chrono", + "serde", + "serde_with", +] + +[[package]] +name = "bytes" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0b3de4a0c5e67e16066a0715723abd91edc2f9001d09c46e1dca929351e130e" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "serde", + "time", + "winapi", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-executor" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" + +[[package]] +name = "futures-macro" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "h2" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" + +[[package]] +name = "httparse" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "hyperlocal" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c" +dependencies = [ + "futures-util", + "hex", + "hyper", + "pin-project", + "tokio", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "matchit" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro2" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71adf41db68aa0daaefc69bb30bcd68ded9b9abaad5d1fbb6304c4fb390e083e" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b670f45da57fb8542ebdbb6105a925fe571b67f9e7ed9f47a06a84e72b4e7cc" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ryu" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" + +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tonic" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9d60db39854b30b835107500cf0aca0b0d14d6e1c3de124217c23a29c2ddb" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "prost-derive", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tower-layer", + "tower-service", + "tracing", + "tracing-futures", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" + +[[package]] +name = "unicode-normalization" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "workload_manager_lib" version = "0.1.0" +dependencies = [ + "bollard", + "futures", + "tokio", + "tonic", +] diff --git a/node-agent/workload_manager/src/workload_manager/mod.rs b/node-agent/workload_manager/src/workload_manager/mod.rs index 8580f7df..6430fa00 100644 --- a/node-agent/workload_manager/src/workload_manager/mod.rs +++ b/node-agent/workload_manager/src/workload_manager/mod.rs @@ -1 +1,2 @@ pub mod workload; +pub mod workload_listener; diff --git a/node-agent/workload_manager/src/workload_manager/workload_listener/containe_listener.rs b/node-agent/workload_manager/src/workload_manager/workload_listener/containe_listener.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/node-agent/workload_manager/src/workload_manager/workload_listener/container_listener.rs b/node-agent/workload_manager/src/workload_manager/workload_listener/container_listener.rs new file mode 100644 index 00000000..12e54f77 --- /dev/null +++ b/node-agent/workload_manager/src/workload_manager/workload_listener/container_listener.rs @@ -0,0 +1,429 @@ +use crate::workload_manager::workload_listener::listener::WorkloadListener; + +use futures::StreamExt; + +use bollard::container::{CPUStats, StatsOptions}; +use bollard::models::{ContainerStateStatusEnum, HealthStatusEnum}; +use bollard::Docker; + +use tokio::runtime::Runtime; +use tonic::Status; + +use proto::agent::{Instance, InstanceStatus, Resource, ResourceSummary, Status as WorkloadStatus}; + +use std::sync::mpsc::Sender; +use sysinfo::{System, SystemExt}; + +#[derive(Clone)] +pub struct ContainerListener {} + +impl ContainerListener { + /// Fetches all the needed data and return it in an InstanceStatus + /// + /// # Arguments + /// + /// * `container_id` - container's id + /// * `instance` - instance's struct + /// * `docker_connection` - bollard's Docker Struct + pub async fn fetch_instance_status( + container_id: &str, + instance: &Instance, + docker_connection: &Docker, + ) -> Result { + let stats_options = Some(StatsOptions { + stream: true, + one_shot: false, + }); + + let lim = match instance.clone().resource { + Some(resource) => match resource.limit { + Some(limit) => limit, + None => return Err(Status::internal("Could not get limit in Instance struct")), + }, + None => return Err(Status::internal("No ressource in Instance struct")), + }; + + let container_data_result = docker_connection + .inspect_container(container_id, None) + .await; + let container_resources_opt = docker_connection + .stats(container_id, stats_options) + .next() + .await; + + if container_resources_opt.is_none() { + return Err(Status::internal(format!( + "Cannot poll stats from workload {} (container {}) ", + instance.id, container_id + ))); + }; + + let container_resources_result = container_resources_opt.unwrap(); + + let container_resources = match container_resources_result { + Ok(res) => res, + Err(e) => return Err(Status::internal(e.to_string())), + }; + + let container_data = match container_data_result { + Ok(res) => res, + Err(e) => return Err(Status::internal(e.to_string())), + }; + + let container_health_opt = container_data.clone().state; + + let mut workload_status = match container_data.state { + Some(state) => match state.status { + Some(container_status) => match container_status { + ContainerStateStatusEnum::RUNNING => WorkloadStatus::Running, + ContainerStateStatusEnum::REMOVING => WorkloadStatus::Destroying, + ContainerStateStatusEnum::EXITED => WorkloadStatus::Terminated, + ContainerStateStatusEnum::DEAD => WorkloadStatus::Crashed, + _ => WorkloadStatus::Running, + }, + None => { + return Err(Status::internal(format!( + "Cannot get status of workload {} (container {}) ", + instance.id, container_id + ))) + } + }, + None => { + return Err(Status::internal(format!( + "Cannot get state of workload {} (container {}) ", + instance.id, container_id + ))) + } + }; + + // In case we have a healthcheck in our container, we can override it with WorkloadStatus::Starting + // https://docs.rs/bollard/latest/bollard/models/struct.Health.html + workload_status = match container_health_opt { + Some(s) => match s.health { + Some(h) => match h.status { + Some(health_enum) => { + if health_enum == HealthStatusEnum::STARTING { + WorkloadStatus::Starting + } else { + workload_status + } + } + None => workload_status, + }, + None => workload_status, + }, + None => workload_status, + }; + + let cpu_usage_in_milli_cpu = ContainerListener::calculate_cpu_usage( + container_resources.cpu_stats, + container_resources.precpu_stats, + ); + + Ok(InstanceStatus { + id: instance.clone().id, + status: workload_status as i32, + description: "heres a description".to_string(), + resource: Some(Resource { + limit: Some(ResourceSummary { + cpu: lim.clone().cpu, + memory: lim.clone().memory, + disk: lim.clone().disk, + }), + usage: Some(ResourceSummary { + cpu: cpu_usage_in_milli_cpu, + memory: container_resources.memory_stats.usage.unwrap_or(0) as i32, + disk: container_resources + .storage_stats + .read_count_normalized + .unwrap_or(0) as i32, + }), + }), + }) + } + + /// Calculates and returns the CPU usage of container in millicpu, https://docs.docker.com/engine/api/v1.41/#tag/Container/operation/ContainerStats + /// + /// # Arguments + /// + /// * `cpu` - cpu usage + /// * `pre_cpu` - pre_cpu usage + fn calculate_cpu_usage(cpu: CPUStats, pre_cpu: CPUStats) -> i32 { + let mut sys = System::new_all(); + sys.refresh_all(); + let cpu_cores_number = match sys.physical_core_count() { + None => 0, + Some(n) => n as u64, + }; + let total_mulli_cpu = cpu_cores_number * 1000; + + let cpu_delta = cpu.cpu_usage.total_usage - pre_cpu.cpu_usage.total_usage; + + let mut cpu_delta_system = cpu.system_cpu_usage.unwrap_or(0); + + //to avoid overflow: u64::abs does not exists and u64::abs_diff is deprecated + cpu_delta_system = if cpu_delta_system >= pre_cpu.system_cpu_usage.unwrap_or(0) { + cpu_delta_system - pre_cpu.system_cpu_usage.unwrap_or(0) + } else { + pre_cpu.system_cpu_usage.unwrap_or(0) - cpu_delta_system + }; + + let cpu_percentage = if cpu_delta_system > 0 { + (cpu_delta / cpu_delta_system) * cpu_cores_number + } else { + 0 + }; + + (total_mulli_cpu * cpu_percentage) as i32 + } +} + +impl WorkloadListener for ContainerListener { + fn new(id: String, instance: Instance, sender: Sender) -> Self { + std::thread::spawn(move || { + #[cfg(unix)] + let docker = Docker::connect_with_socket_defaults().unwrap(); + let rt = Runtime::new().unwrap(); + + loop { + let new_instance_status = rt.block_on(ContainerListener::fetch_instance_status( + id.as_str(), + &instance, + &docker, + )); + match new_instance_status { + Ok(instance_to_send) => { + let status: i32 = instance_to_send.status; + + sender.send(instance_to_send).unwrap(); + + if status == WorkloadStatus::Crashed as i32 + || status == WorkloadStatus::Terminated as i32 + { + break; + } + } + Err(_e) => break, + }; + } + + drop(sender); + }); + + Self {} + } +} + +#[cfg(test)] +mod tests { + use super::WorkloadListener; + use bollard::service::HealthConfig; + use bollard::{container::Config, image::CreateImageOptions, Docker}; + use futures::TryStreamExt; + use proto::agent::Status as WorkloadStatus; + use proto::agent::{Instance, Port, Resource, ResourceSummary, Status, Type as IType}; + use std::sync::mpsc::channel; + + #[tokio::test] + async fn test_fetch() -> Result<(), Status> { + //test setup + #[cfg(unix)] + let docker = Docker::connect_with_socket_defaults().unwrap(); + + docker + .create_image( + Some(CreateImageOptions::<&str> { + from_image: "debian:latest", + ..Default::default() + }), + None, + None, + ) + .try_collect::>() + .await + .unwrap(); + + let health_check = Some(HealthConfig { + test: Some(vec![ + "/bin/echo".to_string(), + "a very well done test".to_string(), + ]), + interval: Some(1000000), + timeout: Some(500000000), + retries: Some(1), + start_period: Some(1000000), + }); + + let cfg = Config { + cmd: Some(vec!["tee"]), + image: Some("debian"), + tty: Some(true), + attach_stdin: Some(false), + attach_stdout: Some(false), + attach_stderr: Some(false), + open_stdin: Some(false), + healthcheck: health_check, + ..Default::default() + }; + + let container = docker + .create_container::<&str, &str>(None, cfg) + .await + .unwrap(); + docker + .start_container::(&container.clone().id, None) + .await + .unwrap(); + + let instance = Instance { + id: "someuuid".to_string(), + name: "somename".to_string(), + r#type: IType::Container as i32, + status: Status::Running as i32, + uri: "http://localhost".to_string(), + environment: vec!["A=0".to_string()], + resource: Some(Resource { + limit: Some(ResourceSummary { + cpu: i32::MAX, + memory: i32::MAX, + disk: i32::MAX, + }), + usage: Some(ResourceSummary { + cpu: 0, + memory: 0, + disk: 0, + }), + }), + ports: vec![Port { + source: 80, + destination: 80, + }], + ip: "127.0.0.1".to_string(), + }; + + let res = super::ContainerListener::fetch_instance_status( + container.id.as_str(), + &instance, + &docker, + ) + .await + .unwrap(); + + docker + .kill_container::(container.id.as_str(), None) + .await + .unwrap(); + docker + .remove_container(container.id.as_str(), None) + .await + .unwrap(); + + assert_eq!(res.id, instance.id); + assert!(res.resource.unwrap().usage.unwrap().cpu >= 0); + + Ok(()) + } + + #[tokio::test] + async fn it_works() -> Result<(), ()> { + //test setup + #[cfg(unix)] + let docker = Docker::connect_with_socket_defaults().unwrap(); + + docker + .create_image( + Some(CreateImageOptions::<&str> { + from_image: "debian:latest", + ..Default::default() + }), + None, + None, + ) + .try_collect::>() + .await + .unwrap(); + + let cfg = Config { + cmd: Some(vec!["/bin/sleep", "5"]), + image: Some("debian"), + tty: Some(true), + attach_stdin: Some(false), + attach_stdout: Some(false), + attach_stderr: Some(false), + open_stdin: Some(false), + ..Default::default() + }; + + let container = docker + .create_container::<&str, &str>(None, cfg) + .await + .unwrap(); + docker + .start_container::(&container.clone().id, None) + .await + .unwrap(); + + let instance = Instance { + id: "someuuid".to_string(), + name: "somename".to_string(), + r#type: IType::Container as i32, + status: Status::Running as i32, + uri: "http://localhost".to_string(), + environment: vec!["A=0".to_string()], + resource: Some(Resource { + limit: Some(ResourceSummary { + cpu: i32::MAX, + memory: i32::MAX, + disk: i32::MAX, + }), + usage: Some(ResourceSummary { + cpu: 0, + memory: 0, + disk: 0, + }), + }), + ports: vec![Port { + source: 80, + destination: 80, + }], + ip: "127.0.0.1".to_string(), + }; + + let (tx, rx) = channel(); + + //test + super::ContainerListener::new(container.clone().id, instance, tx.clone()); + + loop { + let msg = rx.recv(); + + if msg.is_err() { + break; + } + + let received = msg.unwrap(); + let status = received.status as i32; + let received2 = received.clone(); + let received3 = received.clone(); + + println!("{:?}", received); + + assert_eq!(received.resource.unwrap().limit.unwrap().cpu, i32::MAX); + assert!(received2.resource.unwrap().usage.unwrap().cpu >= 0); + assert!(received3.status as i32 <= 8); + + if status == WorkloadStatus::Crashed as i32 + || status == WorkloadStatus::Terminated as i32 + { + break; + } + } + + docker + .remove_container(container.id.as_str(), None) + .await + .unwrap(); + + Ok(()) + } +} diff --git a/node-agent/workload_manager/src/workload_manager/workload_listener/error.rs b/node-agent/workload_manager/src/workload_manager/workload_listener/error.rs new file mode 100644 index 00000000..00c0d161 --- /dev/null +++ b/node-agent/workload_manager/src/workload_manager/workload_listener/error.rs @@ -0,0 +1,27 @@ +use std::error::Error; +use std::fmt; + +#[derive(Debug)] +pub struct WorkloadListenerError { + details: String, +} + +impl WorkloadListenerError { + pub fn new(msg: &str) -> Self { + WorkloadListenerError { + details: msg.to_string(), + } + } +} + +impl fmt::Display for WorkloadListenerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.details) + } +} + +impl Error for WorkloadListenerError { + fn description(&self) -> &str { + self.details.as_str() + } +} diff --git a/node-agent/workload_manager/src/workload_manager/workload_listener/mod.rs b/node-agent/workload_manager/src/workload_manager/workload_listener/mod.rs index e69de29b..878d8c5d 100644 --- a/node-agent/workload_manager/src/workload_manager/workload_listener/mod.rs +++ b/node-agent/workload_manager/src/workload_manager/workload_listener/mod.rs @@ -0,0 +1,7 @@ +pub mod container_listener; +pub mod error; +pub mod vm_listener; + +// To avoid writing ::workload_listener::workload_listener +#[path = "./workload_listener.rs"] +pub mod listener; diff --git a/node-agent/workload_manager/src/workload_manager/workload_listener/vm_listener.rs b/node-agent/workload_manager/src/workload_manager/workload_listener/vm_listener.rs index e69de29b..8b137891 100644 --- a/node-agent/workload_manager/src/workload_manager/workload_listener/vm_listener.rs +++ b/node-agent/workload_manager/src/workload_manager/workload_listener/vm_listener.rs @@ -0,0 +1 @@ + diff --git a/node-agent/workload_manager/src/workload_manager/workload_listener/workload_listener.rs b/node-agent/workload_manager/src/workload_manager/workload_listener/workload_listener.rs new file mode 100644 index 00000000..bd3b91a4 --- /dev/null +++ b/node-agent/workload_manager/src/workload_manager/workload_listener/workload_listener.rs @@ -0,0 +1,13 @@ +use proto::agent::{Instance, InstanceStatus}; +use std::sync::mpsc::Sender; + +pub trait WorkloadListener { + /// Launch a thread that will listen to a workload and send continously an InstanceStatus + /// + /// # Arguments + /// + /// * `id` - A String that is used by the workload's engine to identify it (docker containers' id for instance) + /// * `instance` - An Instance struct, given by the scheduler, + /// * `sender` - A sender given by the WorkloadManager, whose receiver is given to the scheduler + fn new(id: String, instance: Instance, sender: Sender) -> Self; +} From a51e2d0d0a94e5bd9192c7218c4f9d523ac60ff5 Mon Sep 17 00:00:00 2001 From: WoodenMaiden Date: Sat, 27 Aug 2022 18:56:10 +0200 Subject: [PATCH 12/21] feat: add create() abstraction and added result as channel type Signed-off-by: WoodenMaiden --- Cargo.lock | 57 +++++- node-agent/workload_manager/Cargo.toml | 3 + .../workload_listener/container_listener.rs | 167 +++++++++--------- .../workload_listener/error.rs | 27 --- .../workload_manager/workload_listener/mod.rs | 19 +- .../workload_listener/vm_listener.rs | 1 - .../workload_listener/workload_listener.rs | 5 +- 7 files changed, 158 insertions(+), 121 deletions(-) delete mode 100644 node-agent/workload_manager/src/workload_manager/workload_listener/error.rs delete mode 100644 node-agent/workload_manager/src/workload_manager/workload_listener/vm_listener.rs diff --git a/Cargo.lock b/Cargo.lock index 38c15572..c9da0cf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -894,6 +894,21 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab30e97ab6aacfe635fad58f22c2bb06c8b685f7421eb1e064a729e2a5f481fa" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.23" @@ -901,6 +916,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bfc52cbddcfd745bf1740338492bb0bd83d76c67b445f91c5fb29fae29ecaa1" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -909,6 +925,23 @@ version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2acedae88d38235936c3922476b10fced7b2b68136f5e3c03c2d5be348a1115" +[[package]] +name = "futures-executor" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d11aa21b5b587a64682c0094c2bdd4df0076c5324961a40cc3abd7f37930528" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93a66fc6d035a26a3ae255a6d2bca35eda63ae4c5512bef54449113f7a1228e5" + [[package]] name = "futures-macro" version = "0.3.23" @@ -938,9 +971,13 @@ version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0828a5471e340229c11c77ca80017937ce3c58cb788a17e5f1c2d5c485a9577" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -1397,7 +1434,7 @@ name = "node_manager" version = "0.1.0" dependencies = [ "log", - "sysinfo", + "sysinfo 0.24.7", ] [[package]] @@ -2091,6 +2128,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "sysinfo" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71eb43e528fdc239f08717ec2a378fdb017dddbc3412de15fff527554591a66c" +dependencies = [ + "cfg-if 1.0.0", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "rayon", + "winapi", +] + [[package]] name = "system-configuration" version = "0.5.0" @@ -2747,8 +2799,11 @@ version = "0.1.0" dependencies = [ "anyhow", "bollard", + "futures", "futures-util", "proto", + "sysinfo 0.25.3", + "tokio", "tokio-test", "tonic", ] diff --git a/node-agent/workload_manager/Cargo.toml b/node-agent/workload_manager/Cargo.toml index 9023980a..aeb4c000 100644 --- a/node-agent/workload_manager/Cargo.toml +++ b/node-agent/workload_manager/Cargo.toml @@ -10,7 +10,10 @@ proto = { path = "../../proto" } tonic = "0.7" bollard = "0.13" futures-util = "0.3" +futures = "0.3" +tokio = { version= "1", features = ["full"]} anyhow = "1.0" +sysinfo = "0.25.3" [dev-dependencies] tokio-test = "*" \ No newline at end of file diff --git a/node-agent/workload_manager/src/workload_manager/workload_listener/container_listener.rs b/node-agent/workload_manager/src/workload_manager/workload_listener/container_listener.rs index 12e54f77..bc1d8502 100644 --- a/node-agent/workload_manager/src/workload_manager/workload_listener/container_listener.rs +++ b/node-agent/workload_manager/src/workload_manager/workload_listener/container_listener.rs @@ -1,8 +1,9 @@ -use crate::workload_manager::workload_listener::listener::WorkloadListener; +use crate::workload_manager::workload_listener::workload_listener::WorkloadListener; use futures::StreamExt; use bollard::container::{CPUStats, StatsOptions}; +use bollard::errors::Error; use bollard::models::{ContainerStateStatusEnum, HealthStatusEnum}; use bollard::Docker; @@ -11,8 +12,18 @@ use tonic::Status; use proto::agent::{Instance, InstanceStatus, Resource, ResourceSummary, Status as WorkloadStatus}; -use std::sync::mpsc::Sender; use sysinfo::{System, SystemExt}; +use tokio::sync::mpsc::Sender; + +fn convert_status(state: Option) -> WorkloadStatus { + match state.unwrap_or(ContainerStateStatusEnum::RUNNING) { + ContainerStateStatusEnum::RUNNING => WorkloadStatus::Running, + ContainerStateStatusEnum::REMOVING => WorkloadStatus::Destroying, + ContainerStateStatusEnum::EXITED => WorkloadStatus::Terminated, + ContainerStateStatusEnum::DEAD => WorkloadStatus::Crashed, + _ => WorkloadStatus::Running, + } +} #[derive(Clone)] pub struct ContainerListener {} @@ -51,69 +62,57 @@ impl ContainerListener { .next() .await; - if container_resources_opt.is_none() { - return Err(Status::internal(format!( - "Cannot poll stats from workload {} (container {}) ", - instance.id, container_id - ))); - }; - - let container_resources_result = container_resources_opt.unwrap(); - - let container_resources = match container_resources_result { - Ok(res) => res, - Err(e) => return Err(Status::internal(e.to_string())), - }; + let container_resources = container_resources_opt + .ok_or(|e: Error| e) + .map_err( + //At this point we have a Result, Error> so we use map_er two times + |_e| { + Status::internal(format!( + "Cannot get state of workload {} (container {}) ", + instance.id, container_id + )) + }, + )? + .map_err(|e| Status::internal(e.to_string()))?; - let container_data = match container_data_result { - Ok(res) => res, - Err(e) => return Err(Status::internal(e.to_string())), - }; + let container_data = container_data_result.map_err(|e| Status::internal(e.to_string()))?; let container_health_opt = container_data.clone().state; - let mut workload_status = match container_data.state { - Some(state) => match state.status { - Some(container_status) => match container_status { - ContainerStateStatusEnum::RUNNING => WorkloadStatus::Running, - ContainerStateStatusEnum::REMOVING => WorkloadStatus::Destroying, - ContainerStateStatusEnum::EXITED => WorkloadStatus::Terminated, - ContainerStateStatusEnum::DEAD => WorkloadStatus::Crashed, - _ => WorkloadStatus::Running, - }, - None => { - return Err(Status::internal(format!( - "Cannot get status of workload {} (container {}) ", + let mut workload_status = convert_status( + container_data + .state + .ok_or_else(|| { + Status::internal(format!( + "Cannot get state of workload {} (container {}) ", instance.id, container_id - ))) - } - }, - None => { - return Err(Status::internal(format!( - "Cannot get state of workload {} (container {}) ", - instance.id, container_id - ))) - } - }; + )) + })? + .status, + ); // In case we have a healthcheck in our container, we can override it with WorkloadStatus::Starting // https://docs.rs/bollard/latest/bollard/models/struct.Health.html - workload_status = match container_health_opt { - Some(s) => match s.health { - Some(h) => match h.status { - Some(health_enum) => { - if health_enum == HealthStatusEnum::STARTING { - WorkloadStatus::Starting - } else { - workload_status - } - } - None => workload_status, - }, - None => workload_status, + workload_status = container_health_opt.map_or_else( + || workload_status, + |s| { + s.health.map_or_else( + || workload_status, + |h| { + h.status.map_or_else( + || workload_status, + |health_enum| { + if health_enum == HealthStatusEnum::STARTING { + WorkloadStatus::Starting + } else { + workload_status + } + }, + ) + }, + ) }, - None => workload_status, - }; + ); let cpu_usage_in_milli_cpu = ContainerListener::calculate_cpu_usage( container_resources.cpu_stats, @@ -132,11 +131,11 @@ impl ContainerListener { }), usage: Some(ResourceSummary { cpu: cpu_usage_in_milli_cpu, - memory: container_resources.memory_stats.usage.unwrap_or(0) as i32, + memory: container_resources.memory_stats.usage.unwrap_or(0), disk: container_resources .storage_stats .read_count_normalized - .unwrap_or(0) as i32, + .unwrap_or(0), }), }), }) @@ -148,7 +147,7 @@ impl ContainerListener { /// /// * `cpu` - cpu usage /// * `pre_cpu` - pre_cpu usage - fn calculate_cpu_usage(cpu: CPUStats, pre_cpu: CPUStats) -> i32 { + fn calculate_cpu_usage(cpu: CPUStats, pre_cpu: CPUStats) -> u64 { let mut sys = System::new_all(); sys.refresh_all(); let cpu_cores_number = match sys.physical_core_count() { @@ -174,13 +173,13 @@ impl ContainerListener { 0 }; - (total_mulli_cpu * cpu_percentage) as i32 + total_mulli_cpu * cpu_percentage } } impl WorkloadListener for ContainerListener { - fn new(id: String, instance: Instance, sender: Sender) -> Self { - std::thread::spawn(move || { + fn run(id: String, instance: Instance, sender: Sender>) { + std::thread::spawn(move || -> Result<(), Status> { #[cfg(unix)] let docker = Docker::connect_with_socket_defaults().unwrap(); let rt = Runtime::new().unwrap(); @@ -195,7 +194,8 @@ impl WorkloadListener for ContainerListener { Ok(instance_to_send) => { let status: i32 = instance_to_send.status; - sender.send(instance_to_send).unwrap(); + rt.block_on(sender.send(Ok(instance_to_send))) + .map_err(|e| Status::internal(e.to_string()))?; if status == WorkloadStatus::Crashed as i32 || status == WorkloadStatus::Terminated as i32 @@ -203,14 +203,15 @@ impl WorkloadListener for ContainerListener { break; } } - Err(_e) => break, + Err(e) => { + rt.block_on(sender.send(Err(e))).unwrap(); + break; + } }; } - drop(sender); + Ok(()) }); - - Self {} } } @@ -222,7 +223,7 @@ mod tests { use futures::TryStreamExt; use proto::agent::Status as WorkloadStatus; use proto::agent::{Instance, Port, Resource, ResourceSummary, Status, Type as IType}; - use std::sync::mpsc::channel; + use tokio::sync::mpsc::channel; #[tokio::test] async fn test_fetch() -> Result<(), Status> { @@ -284,9 +285,9 @@ mod tests { environment: vec!["A=0".to_string()], resource: Some(Resource { limit: Some(ResourceSummary { - cpu: i32::MAX, - memory: i32::MAX, - disk: i32::MAX, + cpu: u64::MAX, + memory: u64::MAX, + disk: u64::MAX, }), usage: Some(ResourceSummary { cpu: 0, @@ -319,7 +320,7 @@ mod tests { .unwrap(); assert_eq!(res.id, instance.id); - assert!(res.resource.unwrap().usage.unwrap().cpu >= 0); + assert!(res.resource.unwrap().usage.unwrap().cpu < u64::MAX); Ok(()) } @@ -372,9 +373,9 @@ mod tests { environment: vec!["A=0".to_string()], resource: Some(Resource { limit: Some(ResourceSummary { - cpu: i32::MAX, - memory: i32::MAX, - disk: i32::MAX, + cpu: u64::MAX, + memory: u64::MAX, + disk: u64::MAX, }), usage: Some(ResourceSummary { cpu: 0, @@ -389,27 +390,23 @@ mod tests { ip: "127.0.0.1".to_string(), }; - let (tx, rx) = channel(); + let (tx, mut rx) = channel(1000); //test - super::ContainerListener::new(container.clone().id, instance, tx.clone()); + super::ContainerListener::run(container.clone().id, instance, tx.clone()); loop { - let msg = rx.recv(); - - if msg.is_err() { - break; - } + let msg = rx.recv().await; - let received = msg.unwrap(); + let received = msg.unwrap().unwrap(); let status = received.status as i32; let received2 = received.clone(); let received3 = received.clone(); println!("{:?}", received); - assert_eq!(received.resource.unwrap().limit.unwrap().cpu, i32::MAX); - assert!(received2.resource.unwrap().usage.unwrap().cpu >= 0); + assert_eq!(received.resource.unwrap().limit.unwrap().cpu, u64::MAX); + assert!(received2.resource.unwrap().usage.unwrap().cpu < u64::MAX); assert!(received3.status as i32 <= 8); if status == WorkloadStatus::Crashed as i32 diff --git a/node-agent/workload_manager/src/workload_manager/workload_listener/error.rs b/node-agent/workload_manager/src/workload_manager/workload_listener/error.rs deleted file mode 100644 index 00c0d161..00000000 --- a/node-agent/workload_manager/src/workload_manager/workload_listener/error.rs +++ /dev/null @@ -1,27 +0,0 @@ -use std::error::Error; -use std::fmt; - -#[derive(Debug)] -pub struct WorkloadListenerError { - details: String, -} - -impl WorkloadListenerError { - pub fn new(msg: &str) -> Self { - WorkloadListenerError { - details: msg.to_string(), - } - } -} - -impl fmt::Display for WorkloadListenerError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.details) - } -} - -impl Error for WorkloadListenerError { - fn description(&self) -> &str { - self.details.as_str() - } -} diff --git a/node-agent/workload_manager/src/workload_manager/workload_listener/mod.rs b/node-agent/workload_manager/src/workload_manager/workload_listener/mod.rs index 878d8c5d..1f15f47e 100644 --- a/node-agent/workload_manager/src/workload_manager/workload_listener/mod.rs +++ b/node-agent/workload_manager/src/workload_manager/workload_listener/mod.rs @@ -1,7 +1,16 @@ pub mod container_listener; -pub mod error; -pub mod vm_listener; +#[allow(clippy::module_inception)] +pub mod workload_listener; -// To avoid writing ::workload_listener::workload_listener -#[path = "./workload_listener.rs"] -pub mod listener; +use proto::agent::{Instance, InstanceStatus, Type}; +use tokio::sync::mpsc::Sender; +use tonic::Status; +use workload_listener::WorkloadListener; + +use self::container_listener::ContainerListener; + +pub fn create(id: String, instance: Instance, sender: Sender>) { + match instance.r#type() { + Type::Container => ContainerListener::run(id, instance, sender), + } +} diff --git a/node-agent/workload_manager/src/workload_manager/workload_listener/vm_listener.rs b/node-agent/workload_manager/src/workload_manager/workload_listener/vm_listener.rs deleted file mode 100644 index 8b137891..00000000 --- a/node-agent/workload_manager/src/workload_manager/workload_listener/vm_listener.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/node-agent/workload_manager/src/workload_manager/workload_listener/workload_listener.rs b/node-agent/workload_manager/src/workload_manager/workload_listener/workload_listener.rs index bd3b91a4..885ab6bf 100644 --- a/node-agent/workload_manager/src/workload_manager/workload_listener/workload_listener.rs +++ b/node-agent/workload_manager/src/workload_manager/workload_listener/workload_listener.rs @@ -1,5 +1,6 @@ use proto::agent::{Instance, InstanceStatus}; -use std::sync::mpsc::Sender; +use tokio::sync::mpsc::Sender; +use tonic::Status; pub trait WorkloadListener { /// Launch a thread that will listen to a workload and send continously an InstanceStatus @@ -9,5 +10,5 @@ pub trait WorkloadListener { /// * `id` - A String that is used by the workload's engine to identify it (docker containers' id for instance) /// * `instance` - An Instance struct, given by the scheduler, /// * `sender` - A sender given by the WorkloadManager, whose receiver is given to the scheduler - fn new(id: String, instance: Instance, sender: Sender) -> Self; + fn run(id: String, instance: Instance, sender: Sender>); } From 88ecacf1d04110c97c832e0037a66c1cc2dcd66d Mon Sep 17 00:00:00 2001 From: Nils Ponsard Date: Mon, 29 Aug 2022 18:25:09 +0200 Subject: [PATCH 13/21] feat(kudoctl): update workload api calls according to doc Signed-off-by: Nils Ponsard --- kudoctl/src/client/workload.rs | 70 ++++++++++++++++++++---- kudoctl/src/resource/workload.rs | 2 +- kudoctl/src/subcommands/get/resource.rs | 39 +++++++------ kudoctl/src/subcommands/get/resources.rs | 8 +-- 4 files changed, 83 insertions(+), 36 deletions(-) diff --git a/kudoctl/src/client/workload.rs b/kudoctl/src/client/workload.rs index 7ccabffc..4e7c1842 100644 --- a/kudoctl/src/client/workload.rs +++ b/kudoctl/src/client/workload.rs @@ -1,15 +1,32 @@ use anyhow::{Context, Result}; -use log::debug; +use log::{debug, warn}; use reqwest::Method; use serde::{Deserialize, Serialize}; use crate::{ client::types::IdResponse, - resource::{self, workload}, + resource::workload::{self, Resources}, }; use super::request::{Client, RequestError}; +/// Ports binding for the workload +#[derive(Debug, Deserialize, Serialize)] +pub struct PortBinding { + pub source: i32, + pub destination: i32, +} + +/// Workload, as stored in the controller +#[derive(Debug, Deserialize, Serialize)] +pub struct WorkloadBody { + pub name: String, + pub uri: String, + pub environment: Vec, + pub resources: Resources, + pub ports: Vec, +} + /// Creates a workload in the cluster. /// /// Returns the id of the workload. @@ -18,11 +35,46 @@ pub async fn create( namespace: &str, workload: &workload::Workload, ) -> std::result::Result { + let workload_body = WorkloadBody { + name: workload.name.clone(), + uri: workload.uri.clone(), + environment: workload.env.as_deref().unwrap_or_default().to_vec(), + resources: workload.resources.to_owned(), + ports: workload + .ports + .to_owned() + .unwrap_or_default() + .into_iter() + .map(|p| { + // parse the port binding from the string + + let mut splitted = p.split(':'); + let source = splitted.next().unwrap_or(&p).parse::().unwrap_or(0); + + if source == 0 { + warn!("Invalid port binding: {}", p); + } + + let destination = if let Some(s) = splitted.nth(1) { + debug!("No destination port specified, using source port"); + s.parse::().unwrap_or(source) + } else { + source + }; + + PortBinding { + source, + destination, + } + }) + .collect(), + }; + let response: IdResponse = (*client) .send_json_request( format!("/workload/{}", namespace).as_str(), Method::PUT, - Some(workload), + Some(&workload_body), ) .await?; debug!("Workload {} created", response.id); @@ -51,13 +103,9 @@ pub async fn update( /// Get info about a workload. /// /// Returns the workload info. -pub async fn get( - client: &Client, - namespace: &str, - workload_id: &str, -) -> Result { - let response: workload::Workload = (*client) - .send_json_request::( +pub async fn get(client: &Client, namespace: &str, workload_id: &str) -> Result { + let response: WorkloadBody = (*client) + .send_json_request::( &format!("/workload/{}/{}", namespace, workload_id), Method::GET, None, @@ -70,7 +118,7 @@ pub async fn get( #[derive(Debug, Deserialize, Serialize)] pub struct GetWorkloadResponse { pub count: u64, - pub workloads: Vec, + pub workloads: Vec, #[serde(skip)] pub show_header: bool, } diff --git a/kudoctl/src/resource/workload.rs b/kudoctl/src/resource/workload.rs index ea255018..ff42cc89 100644 --- a/kudoctl/src/resource/workload.rs +++ b/kudoctl/src/resource/workload.rs @@ -13,7 +13,7 @@ pub struct Workload { } // Resources assigned to a workload -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct Resources { // CPU in milliCPU pub cpu: u64, diff --git a/kudoctl/src/subcommands/get/resource.rs b/kudoctl/src/subcommands/get/resource.rs index e3e3cdfd..86099419 100644 --- a/kudoctl/src/subcommands/get/resource.rs +++ b/kudoctl/src/subcommands/get/resource.rs @@ -1,8 +1,7 @@ use super::output::{self, OutputFormat}; use crate::{ - client::{self, request::Client}, + client::{self, request::Client, workload::WorkloadBody}, config, - resource::workload::Workload, }; use anyhow::{bail, Context, Result}; use std::fmt::Display; @@ -25,29 +24,33 @@ pub async fn execute( output::format_output(result, format) } -impl Display for Workload { +impl Display for WorkloadBody { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "name : {}\n", self.name)?; writeln!(f, "uri : {}\n", self.uri)?; - if let Some(ports) = &self.ports { - // display ports - let ports_str = ports - .iter() - .fold(String::new(), |acc, port| acc + &format!("{} ", port)) - .trim() - .replace(' ', ",") - .replace(':', "->"); + // display ports + let ports_str = self + .ports + .iter() + .fold(String::new(), |acc, port| { + acc + &format!("{}->{} ", port.source, port.destination) + }) + .trim() + .replace(' ', ","); + + if !ports_str.is_empty() { writeln!(f, "ports : {} ", ports_str)?; } - if let Some(env) = &self.env { - // display environment variables - let env_vars_str = env - .iter() - .fold(String::new(), |acc, env_var| acc + &format!("{} ", env_var)) - .trim() - .replace(' ', ","); + // display environment variables + let env_vars_str = self + .environment + .iter() + .fold(String::new(), |acc, env_var| acc + &format!("{} ", env_var)) + .trim() + .replace(' ', ","); + if !env_vars_str.is_empty() { writeln!(f, "env variables : {} ", env_vars_str)?; } diff --git a/kudoctl/src/subcommands/get/resources.rs b/kudoctl/src/subcommands/get/resources.rs index 2edede0c..f2bc0808 100644 --- a/kudoctl/src/subcommands/get/resources.rs +++ b/kudoctl/src/subcommands/get/resources.rs @@ -1,6 +1,6 @@ use crate::{ client::{self, request::Client, workload::GetWorkloadResponse}, - config, resource, + config, }; use anyhow::{Context, Result}; use std::fmt::Display; @@ -26,11 +26,7 @@ impl Display for GetWorkloadResponse { } for r in &self.workloads { - match r { - resource::Resource::Workload(workload) => { - writeln!(f, "{}\tWorkload\n", workload.name)?; - } - } + writeln!(f, "{}\tWorkload\n", r.name)?; } Ok(()) } From 99043f961e30e5a5202c910bbd4d22a0e30f346d Mon Sep 17 00:00:00 2001 From: Alexis Langlet Date: Mon, 29 Aug 2022 22:26:19 +0200 Subject: [PATCH 14/21] feat: add limits to container Limit resource usage of container. CPU and memory can be limited but this is not possible for disk usage yet. Signed-off-by: Alexis Langlet --- .../workload_manager/workload/container.rs | 96 +++++++++++++------ 1 file changed, 65 insertions(+), 31 deletions(-) diff --git a/node-agent/workload_manager/src/workload_manager/workload/container.rs b/node-agent/workload_manager/src/workload_manager/workload/container.rs index 553beabd..1e56532d 100644 --- a/node-agent/workload_manager/src/workload_manager/workload/container.rs +++ b/node-agent/workload_manager/src/workload_manager/workload/container.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use bollard::container::{ Config, KillContainerOptions, RemoveContainerOptions, RenameContainerOptions, StopContainerOptions, @@ -17,9 +19,7 @@ pub struct Container { } impl Container { - // - // Create a new workload (container) and start it - // + /// Create a new workload (container) and start it pub async fn new(instance: Instance) -> Result { let docker = Docker::connect_with_socket_defaults().context("Can't connect to docker socket. ")?; @@ -37,27 +37,7 @@ impl Container { .await .context("Can't create image. ")?; - let container_config: Config<&str> = Config { - image: Some(instance.uri.as_str()), - tty: Some(true), - ..Default::default() - }; - - let container_id = docker - .create_container::<&str, &str>(None, container_config) - .await - .context("Can't create container. ")? - .id; - - docker - .rename_container( - container_id.as_str(), - RenameContainerOptions { - name: instance.name, - }, - ) - .await - .ok(); + let container_id = create_container(&docker, instance).await?; docker .start_container::(container_id.as_str(), None) @@ -67,9 +47,7 @@ impl Container { Ok(Container { id: container_id }) } - // - // Removes a container - // + /// Removes a container async fn remove(&self) -> Result<(), Error> { let docker = Docker::connect_with_socket_defaults().context("Can't connect to docker socket. ")?; @@ -94,9 +72,7 @@ impl Workload for Container { self.id.to_string() } - // // Gracefully stop a workload - // async fn stop(&self) -> Result<(), Error> { let docker = Docker::connect_with_socket_defaults().context("Can't connect to docker socket. ")?; @@ -116,10 +92,8 @@ impl Workload for Container { Ok(()) } - // // Force a workload to stop // (equivalent to a `kill -9` on linux) - // async fn kill(&self) -> Result<(), Error> { let docker = Docker::connect_with_socket_defaults().context("Can't connect to docker socket. ")?; @@ -138,6 +112,66 @@ impl Workload for Container { } } +/// It creates a container with the given instance's configuration +/// +/// Arguments: +/// +/// * `docker`: &Docker - This is the docker client that we created earlier. +/// * `instance`: Instance +/// +/// Returns: +/// +/// A string that is the container id. +async fn create_container(docker: &Docker, instance: Instance) -> Result { + let mut ports = HashMap::new(); + let port_list = &instance.ports; + for port in port_list { + ports.insert( + port.destination.to_string(), + Some(vec![bollard::service::PortBinding { + host_port: Some(port.destination.to_string()), + ..Default::default() + }]), + ); + } + + let container_config: Config<&str> = Config { + image: Some(instance.uri.as_str()), + tty: Some(true), + host_config: Some(bollard::service::HostConfig { + port_bindings: Some(ports), + nano_cpus: instance + .resource + .clone() + .and_then(|resource| resource.limit.map(|limit| limit.cpu.try_into().unwrap())), + memory: instance + .resource + .clone() + .and_then(|resource| resource.limit.map(|limit| limit.memory.try_into().unwrap())), + ..Default::default() + }), + ..Default::default() + }; + + let container_id = docker + .create_container::<&str, &str>(None, container_config) + .await + .context("Can't create container. ")? + .id; + + docker + .rename_container( + container_id.as_str(), + RenameContainerOptions { + name: instance.name, + }, + ) + .await + .ok(); + + Ok(container_id) +} + #[cfg(test)] mod tests { use crate::workload_manager::workload::workload_trait::Workload; From 60677aaeb62e1404f7faa9d6145e6c8ba8e93bd8 Mon Sep 17 00:00:00 2001 From: Maxime Date: Tue, 30 Aug 2022 10:02:27 +0200 Subject: [PATCH 15/21] feat : create namespace service Namespace service implements all the logic to manage Namespace. Workload service implements EtcdService which is the interface to interact with etcd. Things to know : - Namespace service require etcd address when initialized - Etcd save in key => value format. The key is the word "namespace" concatenated with the namespace name Signed-off-by: Maxime --- controller/lib/Cargo.toml | 1 - .../lib/src/external_api/namespace/mod.rs | 3 + .../lib/src/external_api/namespace/model.rs | 72 +++++++++++ .../lib/src/external_api/namespace/service.rs | 117 ++++++++++++++++++ 4 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 controller/lib/src/external_api/namespace/mod.rs create mode 100644 controller/lib/src/external_api/namespace/model.rs create mode 100644 controller/lib/src/external_api/namespace/service.rs diff --git a/controller/lib/Cargo.toml b/controller/lib/Cargo.toml index 91ad2406..02c2caab 100644 --- a/controller/lib/Cargo.toml +++ b/controller/lib/Cargo.toml @@ -13,6 +13,5 @@ tonic = "0.7.2" proto = { path = "../../proto" } log = "0.4.0" tokio = { version = "1.20.0", features = ["rt-multi-thread", "macros"] } - serde_json = "1.0" diff --git a/controller/lib/src/external_api/namespace/mod.rs b/controller/lib/src/external_api/namespace/mod.rs new file mode 100644 index 00000000..9c5ae537 --- /dev/null +++ b/controller/lib/src/external_api/namespace/mod.rs @@ -0,0 +1,3 @@ +pub mod controller; +pub mod model; +pub mod service; diff --git a/controller/lib/src/external_api/namespace/model.rs b/controller/lib/src/external_api/namespace/model.rs new file mode 100644 index 00000000..41abcd0c --- /dev/null +++ b/controller/lib/src/external_api/namespace/model.rs @@ -0,0 +1,72 @@ +use actix_web::HttpResponse; +use serde::{Deserialize, Serialize}; + +pub enum NamespaceError { + NotFound, + Etcd(String), + NameAlreadyExists(String), + JsonToNamespace(String), + NamespaceToJson(String), +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct Metadata {} +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct Namespace { + pub id: String, + pub name: String, + pub metadata: Metadata, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct NamespaceDTO { + pub name: String, +} + +impl NamespaceError { + pub fn to_http(&self) -> HttpResponse { + match self { + NamespaceError::NotFound => HttpResponse::NotFound().body("Namespace not found"), + NamespaceError::Etcd(err) => { + HttpResponse::InternalServerError().body(format!("Etcd error: {} ", err)) + } + NamespaceError::NameAlreadyExists(name) => HttpResponse::Conflict() + .body(format!("Namespace with name {} already exists", name)), + NamespaceError::JsonToNamespace(err) => HttpResponse::InternalServerError().body( + format!("Error while converting JSON string to Namespace : {}", err), + ), + NamespaceError::NamespaceToJson(err) => HttpResponse::InternalServerError().body( + format!("Error while converting the Namespace to JSON: {}", err), + ), + } + } +} +impl Namespace { + pub fn to_http(&self) -> HttpResponse { + match serde_json::to_string(&self) { + Ok(json) => HttpResponse::Ok().body(json), + Err(err) => HttpResponse::InternalServerError().body(format!( + "Error while converting the Namespace to JSON: {}", + err + )), + } + } +} +#[derive(Deserialize, Serialize)] +pub struct NamespaceVector { + pub namespaces: Vec, +} +impl NamespaceVector { + pub fn new(namespaces: Vec) -> NamespaceVector { + NamespaceVector { namespaces } + } + pub fn to_http(&self) -> HttpResponse { + match serde_json::to_string(&self.namespaces) { + Ok(json) => HttpResponse::Ok().body(json), + Err(err) => HttpResponse::InternalServerError().body(format!( + "Error while converting the Namespace to JSON: {}", + err + )), + } + } +} diff --git a/controller/lib/src/external_api/namespace/service.rs b/controller/lib/src/external_api/namespace/service.rs new file mode 100644 index 00000000..d85e9ea6 --- /dev/null +++ b/controller/lib/src/external_api/namespace/service.rs @@ -0,0 +1,117 @@ +use crate::etcd::EtcdClient; +use crate::external_api::generic::filter::FilterService; +use serde_json; +use std::net::SocketAddr; + +use super::model::{Metadata, Namespace, NamespaceDTO, NamespaceError, NamespaceVector}; + +pub struct NamespaceService { + etcd_service: EtcdClient, + filter_service: FilterService, +} + +impl NamespaceService { + pub async fn new(etcd_address: &SocketAddr) -> Result { + let inner = NamespaceService { + etcd_service: EtcdClient::new(etcd_address.to_string()) + .await + .map_err(|err| NamespaceError::Etcd(err.to_string()))?, + filter_service: FilterService::new(), + }; + Ok(inner) + } + + pub async fn namespace(&mut self, namespace_name: &str) -> Result { + let id = self.id(namespace_name); + match self.etcd_service.get(&id).await { + Some(namespace) => { + let namespace: Namespace = serde_json::from_str(&namespace) + .map_err(|err| NamespaceError::JsonToNamespace(err.to_string()))?; + Ok(namespace) + } + None => Err(NamespaceError::NotFound), + } + } + + pub async fn get_all_namespace(&mut self, limit: u32, offset: u32) -> NamespaceVector { + let mut new_vec: Vec = Vec::new(); + match self.etcd_service.get_all().await { + Some(namespaces) => { + for namespace in namespaces { + // if namespace deserialize failed , we don't want to throw error , so we just don't add it to the vector + if let Ok(namespace) = serde_json::from_str::(&namespace) { + new_vec.push(namespace); + } + } + if offset > 0 { + match self.filter_service.offset(&new_vec, offset) { + Ok(namespaces) => new_vec = namespaces, + Err(_) => return NamespaceVector::new(vec![]), + } + } + if limit > 0 { + new_vec = self.filter_service.limit(&new_vec, limit); + } + NamespaceVector::new(new_vec) + } + None => NamespaceVector::new(vec![]), + } + } + + pub async fn create_namespace( + &mut self, + namespace_dto: NamespaceDTO, + ) -> Result { + let id = self.id(&namespace_dto.name); + match self.namespace(&namespace_dto.name).await { + Ok(namespace) => Err(NamespaceError::NameAlreadyExists(namespace.name)), + Err(err) => match err { + NamespaceError::NotFound => { + let namespace = Namespace { + id: id.to_string(), + name: namespace_dto.name, + metadata: Metadata {}, + }; + let json = serde_json::to_string(&namespace) + .map_err(|err| NamespaceError::NamespaceToJson(err.to_string()))?; + self.etcd_service + .put(&id, &json) + .await + .map_err(|err| NamespaceError::Etcd(err.to_string()))?; + Ok(namespace) + } + _ => Err(err), + }, + } + } + + pub async fn update_namespace( + &mut self, + namespace_dto: NamespaceDTO, + namespace_name: &str, + ) -> Result { + // we get the id before update , and the new id after update + let new_id = self.id(&namespace_dto.name); + self.namespace(namespace_name).await?; + let namespace = Namespace { + id: new_id.to_string(), + name: namespace_dto.name, + metadata: Metadata {}, + }; + let json = serde_json::to_string(&namespace) + .map_err(|err| NamespaceError::NamespaceToJson(err.to_string()))?; + self.etcd_service + .put(&new_id, &json) + .await + .map_err(|err| NamespaceError::Etcd(err.to_string()))?; + Ok(namespace) + } + + pub async fn delete_namespace(&mut self, namespace_name: &str) { + let id = self.id(namespace_name); + _ = self.etcd_service.delete(&id).await; + } + pub fn id(&mut self, namespace_name: &str) -> String { + format!("namespace.{}", namespace_name) + } +} From a0057950008b8282ac58a388ffdc15dc22dca7e1 Mon Sep 17 00:00:00 2001 From: Maxime Date: Tue, 30 Aug 2022 10:05:27 +0200 Subject: [PATCH 16/21] feat : add namespace controller Add routes and routes handler for namespace. Controller use namespace service. - /namespace/ (GET) : Get all namespace - /namespace (PUT) : Create a new namespace - /namespace// (DELETE) : Delete a namespace - /namespace// (GET) : Get a namespace by name - /namespace// (PATCH) : Update a namespace Things to know - function `services` is called by the server to get all the routes it needs to handle. It associates routes with middleware functions - pagination is used on this route : /namespace offset : start from the element limit : retrieve a maximum of elements - When using pagination in get all route , query argument are optional but if they are used it is necessary to specify the two arguments (limit and offset). It is not possible to specify a single argument on both . Signed-off-by: Maxime --- controller/lib/src/external_api/interface.rs | 2 + controller/lib/src/external_api/mod.rs | 1 + .../src/external_api/namespace/controller.rs | 113 ++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 controller/lib/src/external_api/namespace/controller.rs diff --git a/controller/lib/src/external_api/interface.rs b/controller/lib/src/external_api/interface.rs index 9f2928ba..ad9ecfa5 100644 --- a/controller/lib/src/external_api/interface.rs +++ b/controller/lib/src/external_api/interface.rs @@ -1,3 +1,4 @@ +use super::namespace; use super::workload; use actix_web::middleware::Logger; use actix_web::{web, App, HttpResponse, HttpServer}; @@ -22,6 +23,7 @@ impl ExternalAPIInterface { .app_data(web::Data::new(ActixAppState { etcd_address })) .route("/health", web::get().to(HttpResponse::Ok)) .service(workload::controller::WorkloadController {}.services()) + .service(namespace::controller::NamespaceController {}.services()) .wrap(Logger::default()) }) .workers(num_workers) diff --git a/controller/lib/src/external_api/mod.rs b/controller/lib/src/external_api/mod.rs index 4a6b9205..88a23bfa 100644 --- a/controller/lib/src/external_api/mod.rs +++ b/controller/lib/src/external_api/mod.rs @@ -1,3 +1,4 @@ pub mod generic; pub mod interface; +mod namespace; mod workload; diff --git a/controller/lib/src/external_api/namespace/controller.rs b/controller/lib/src/external_api/namespace/controller.rs new file mode 100644 index 00000000..20276b84 --- /dev/null +++ b/controller/lib/src/external_api/namespace/controller.rs @@ -0,0 +1,113 @@ +use crate::external_api::generic::model::Pagination; +use crate::external_api::interface::ActixAppState; + +use super::model::NamespaceDTO; +use super::service::NamespaceService; +use actix_web::http::StatusCode; +use actix_web::{web, HttpResponse, Responder, Scope}; +pub struct NamespaceController {} +impl NamespaceController { + pub fn services(&self) -> Scope { + web::scope("/namespace") + .service( + web::resource("/{namespace_name}") + .route(web::delete().to(NamespaceController::delete_namespace)) + .route(web::get().to(NamespaceController::namespace)) + .route(web::patch().to(NamespaceController::patch_namespace)), + ) + .service( + web::resource("") + .route(web::put().to(NamespaceController::put_namespace)) + .route(web::get().to(NamespaceController::get_all_namespace)), + ) + } + + pub async fn namespace( + params: web::Path, + data: web::Data, + ) -> impl Responder { + let mut namespace_service = match NamespaceService::new(&data.etcd_address).await { + Ok(namespace) => namespace, + Err(e) => return e.to_http(), + }; + + let namespace_name = params.into_inner(); + + namespace_service + .namespace(&namespace_name) + .await + .map_or_else(|e| e.to_http(), |w| w.to_http()) + } + + pub async fn put_namespace( + body: web::Json, + data: web::Data, + ) -> impl Responder { + let mut namespace_service = match NamespaceService::new(&data.etcd_address).await { + Ok(namespace) => namespace, + Err(e) => return e.to_http(), + }; + let namespace_dto = body.into_inner(); + namespace_service + .create_namespace(namespace_dto) + .await + .map_or_else(|e| e.to_http(), |w| w.to_http()) + } + + pub async fn get_all_namespace( + pagination: Option>, + data: web::Data, + ) -> impl Responder { + let mut namespace_service = match NamespaceService::new(&data.etcd_address).await { + Ok(namespace) => namespace, + Err(e) => return e.to_http(), + }; + + match pagination { + Some(pagination) => { + let namespaces = namespace_service + .get_all_namespace(pagination.limit, pagination.offset) + .await; + namespaces.to_http() + } + None => { + let namespaces = namespace_service.get_all_namespace(0, 0).await; + namespaces.to_http() + } + } + } + + pub async fn patch_namespace( + params: web::Path, + body: web::Json, + data: web::Data, + ) -> impl Responder { + let mut namespace_service = match NamespaceService::new(&data.etcd_address).await { + Ok(namespace) => namespace, + Err(e) => return e.to_http(), + }; + + let namespace_name = params.into_inner(); + let namespace_dto = body.into_inner(); + + namespace_service + .update_namespace(namespace_dto, &namespace_name) + .await + .map_or_else(|e| e.to_http(), |w| w.to_http()) + } + + pub async fn delete_namespace( + params: web::Path, + data: web::Data, + ) -> impl Responder { + let mut namespace_service = match NamespaceService::new(&data.etcd_address).await { + Ok(namespace) => namespace, + Err(e) => return e.to_http(), + }; + + let namespace_name = params.into_inner(); + + namespace_service.delete_namespace(&namespace_name).await; + HttpResponse::build(StatusCode::NO_CONTENT).body("Remove successfully") + } +} From 2dc7bbe5890459d84ec8af53cdc5265194907640 Mon Sep 17 00:00:00 2001 From: Maxime Date: Tue, 30 Aug 2022 10:23:47 +0200 Subject: [PATCH 17/21] fix : namespace for workload Now namespace service is created , we add it to workload service to check if namespace exist when managing workloads. Signed-off-by: Maxime --- controller/lib/src/external_api/workload/model.rs | 9 ++++++++- .../lib/src/external_api/workload/service.rs | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/controller/lib/src/external_api/workload/model.rs b/controller/lib/src/external_api/workload/model.rs index eb376bf8..a8d79797 100644 --- a/controller/lib/src/external_api/workload/model.rs +++ b/controller/lib/src/external_api/workload/model.rs @@ -7,6 +7,8 @@ pub enum WorkloadError { NameAlreadyExists(String), JsonToWorkload(String), WorkloadToJson(String), + NamespaceService, + NamespaceNotFound, } impl WorkloadError { @@ -25,6 +27,11 @@ impl WorkloadError { WorkloadError::WorkloadToJson(err) => HttpResponse::InternalServerError().body( format!("Error while converting the workload to JSON: {}", err), ), + WorkloadError::NamespaceService => HttpResponse::InternalServerError() + .body("Cannot create a NamespaceService instance"), + WorkloadError::NamespaceNotFound => { + HttpResponse::NotFound().body("Namespace not found") + } } } } @@ -82,7 +89,7 @@ impl WorkloadVector { WorkloadVector { workloads } } pub fn to_http(&self) -> HttpResponse { - match serde_json::to_string(&self) { + match serde_json::to_string(&self.workloads) { Ok(json) => HttpResponse::Ok().body(json), Err(err) => HttpResponse::InternalServerError().body(format!( "Error while converting the workload to json: {}", diff --git a/controller/lib/src/external_api/workload/service.rs b/controller/lib/src/external_api/workload/service.rs index 9cb98aca..7e0fa942 100644 --- a/controller/lib/src/external_api/workload/service.rs +++ b/controller/lib/src/external_api/workload/service.rs @@ -1,9 +1,9 @@ -use std::net::SocketAddr; - use super::model::{Ressources, Type, Workload, WorkloadDTO, WorkloadError, WorkloadVector}; use crate::etcd::EtcdClient; use crate::external_api::generic::filter::FilterService; +use crate::external_api::namespace::service::NamespaceService; use serde_json; +use std::net::SocketAddr; /// `WorkloadService` is a struct that inpired from Controllers Provider Modules architectures. It can be used as a service in the WorkloadController .A service can use other services. /// Properties: @@ -13,6 +13,7 @@ use serde_json; pub struct WorkloadService { etcd_service: EtcdClient, filter_service: FilterService, + namespace_service: NamespaceService, } impl WorkloadService { @@ -22,6 +23,9 @@ impl WorkloadService { .await .map_err(|err| WorkloadError::Etcd(err.to_string()))?, filter_service: FilterService::new(), + namespace_service: NamespaceService::new(etcd_address) + .await + .map_err(|_| WorkloadError::NamespaceService)?, }; Ok(inner) } @@ -31,6 +35,12 @@ impl WorkloadService { namespace: &str, ) -> Result { let id = self.id(workload_name, namespace); + //check if namespace exists + self.namespace_service + .namespace(namespace) + .await + .map_err(|_| WorkloadError::NamespaceNotFound)?; + match self.etcd_service.get(&id).await { Some(workload) => { let workload: Workload = serde_json::from_str(&workload) From d2935f50c278f88c550cd5fea4ddc02e47a9cc20 Mon Sep 17 00:00:00 2001 From: Alexis Langlet Date: Tue, 30 Aug 2022 16:00:11 +0200 Subject: [PATCH 18/21] feat: add networking to container Update workload creation in order to save network settings Update container in order to connect to specific bridge and use specified ip Signed-off-by: Alexis Langlet --- Cargo.lock | 25 +++ network/src/utils/mod.rs | 2 +- node-agent/workload_manager/Cargo.toml | 11 ++ .../workload_manager/workload/container.rs | 144 ++++++++++++++---- .../src/workload_manager/workload/mod.rs | 11 +- .../workload/workload_runner.rs | 51 +++++++ .../workload/workload_trait.rs | 10 +- 7 files changed, 209 insertions(+), 45 deletions(-) create mode 100644 node-agent/workload_manager/src/workload_manager/workload/workload_runner.rs diff --git a/Cargo.lock b/Cargo.lock index c9da0cf5..407c7f37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2546,6 +2546,28 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "uuid" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" +dependencies = [ + "getrandom", + "rand", + "uuid-macro-internal", +] + +[[package]] +name = "uuid-macro-internal" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548f7181a5990efa50237abb7ebca410828b57a8955993334679f8b50b35c97d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -2799,13 +2821,16 @@ version = "0.1.0" dependencies = [ "anyhow", "bollard", + "cidr", "futures", "futures-util", + "network", "proto", "sysinfo 0.25.3", "tokio", "tokio-test", "tonic", + "uuid", ] [[package]] diff --git a/network/src/utils/mod.rs b/network/src/utils/mod.rs index d5d8f918..740a9620 100644 --- a/network/src/utils/mod.rs +++ b/network/src/utils/mod.rs @@ -8,7 +8,7 @@ use crate::error::KudoNetworkError; const IFACE_MAX_SIZE: usize = 12; -pub(crate) fn bridge_name(node_id: String) -> String { +pub fn bridge_name(node_id: String) -> String { format!("kbr{}", &node_id[..min(IFACE_MAX_SIZE, node_id.len())]) } diff --git a/node-agent/workload_manager/Cargo.toml b/node-agent/workload_manager/Cargo.toml index aeb4c000..d08a2633 100644 --- a/node-agent/workload_manager/Cargo.toml +++ b/node-agent/workload_manager/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] proto = { path = "../../proto" } +network = { path = "../../network" } tonic = "0.7" bollard = "0.13" futures-util = "0.3" @@ -14,6 +15,16 @@ futures = "0.3" tokio = { version= "1", features = ["full"]} anyhow = "1.0" sysinfo = "0.25.3" +cidr = "0.2.1" + +[dependencies.uuid] +version = "1.1.2" +features = [ + "v4", # Lets you generate random UUIDs + "fast-rng", # Use a faster (but still sufficiently random) RNG + "macro-diagnostics", # Enable better diagnostics for compile-time UUIDs +] + [dev-dependencies] tokio-test = "*" \ No newline at end of file diff --git a/node-agent/workload_manager/src/workload_manager/workload/container.rs b/node-agent/workload_manager/src/workload_manager/workload/container.rs index 1e56532d..7bf96f4b 100644 --- a/node-agent/workload_manager/src/workload_manager/workload/container.rs +++ b/node-agent/workload_manager/src/workload_manager/workload/container.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use bollard::container::{ - Config, KillContainerOptions, RemoveContainerOptions, RenameContainerOptions, + Config, KillContainerOptions, NetworkingConfig, RemoveContainerOptions, RenameContainerOptions, StopContainerOptions, }; use bollard::Docker; @@ -9,8 +9,10 @@ use bollard::Docker; use anyhow::{Context, Error, Result}; use bollard::image::CreateImageOptions; +use bollard::service::EndpointSettings; use futures_util::TryStreamExt; +use super::workload_runner::NetworkSettings; use super::workload_trait::Workload; use proto::agent::Instance; @@ -20,7 +22,10 @@ pub struct Container { impl Container { /// Create a new workload (container) and start it - pub async fn new(instance: Instance) -> Result { + pub async fn new( + instance: Instance, + network_settings: &NetworkSettings, + ) -> Result { let docker = Docker::connect_with_socket_defaults().context("Can't connect to docker socket. ")?; @@ -37,7 +42,7 @@ impl Container { .await .context("Can't create image. ")?; - let container_id = create_container(&docker, instance).await?; + let container_id = create_container(&docker, instance, network_settings).await?; docker .start_container::(container_id.as_str(), None) @@ -122,7 +127,11 @@ impl Workload for Container { /// Returns: /// /// A string that is the container id. -async fn create_container(docker: &Docker, instance: Instance) -> Result { +async fn create_container( + docker: &Docker, + instance: Instance, + network_settings: &NetworkSettings, +) -> Result { let mut ports = HashMap::new(); let port_list = &instance.ports; for port in port_list { @@ -135,6 +144,8 @@ async fn create_container(docker: &Docker, instance: Instance) -> Result ); } + let random_id = uuid::Uuid::new_v4().to_string(); + let container_config: Config<&str> = Config { image: Some(instance.uri.as_str()), tty: Some(true), @@ -148,8 +159,19 @@ async fn create_container(docker: &Docker, instance: Instance) -> Result .resource .clone() .and_then(|resource| resource.limit.map(|limit| limit.memory.try_into().unwrap())), + ..Default::default() }), + networking_config: Some(NetworkingConfig { + endpoints_config: HashMap::from([( + random_id.as_str(), + EndpointSettings { + links: Some(vec![network_settings.bridge_name.clone()]), + ip_address: Some(instance.ip), + ..Default::default() + }, + )]), + }), ..Default::default() }; @@ -174,19 +196,35 @@ async fn create_container(docker: &Docker, instance: Instance) -> Result #[cfg(test)] mod tests { - use crate::workload_manager::workload::workload_trait::Workload; + use std::str::FromStr; + + use crate::workload_manager::workload::{ + workload_runner::NetworkSettings, workload_trait::Workload, + }; use super::Container; use anyhow::{Error, Result}; - use bollard::{ - container::{ListContainersOptions, RemoveContainerOptions}, - Docker, + use bollard::{container::ListContainersOptions, Docker}; + use cidr::Ipv4Inet; + use network::node::{ + clean_node, + request::{CleanNodeRequest, SetupNodeRequest}, + setup_node, }; use proto::agent::{Instance, Resource, ResourceSummary, Type}; const IMAGE: &str = "alpine:3"; - async fn create_default_container() -> Result { + /// Creates a container used for test + /// + /// Arguments: + /// + /// * `node_id`: The ID of the node that the container is running on. + /// + /// Returns: + /// + /// A container + async fn create_default_container(node_id: String) -> Result { let resource: Resource = Resource { limit: Some(ResourceSummary { cpu: 0, @@ -200,10 +238,12 @@ mod tests { }), }; + let instance_id = node_id.clone(); + let instance = Instance { - id: "0".to_string(), + id: instance_id, name: "test_container".to_string(), - ip: "".to_string(), + ip: "127.0.0.1/32".to_string(), uri: IMAGE.to_string(), environment: Vec::new(), ports: Vec::new(), @@ -212,11 +252,58 @@ mod tests { r#type: Type::Container.into(), }; - Ok(Container::new(instance).await?) + Ok(Container::new( + instance, + &NetworkSettings { + node_id: node_id.clone(), + bridge_name: node_id, + }, + ) + .await?) + } + + /// It sets up a node, runs a test, and cleans up the node + /// + /// Arguments: + /// + /// * `test`: The test function to run. + /// * `node_id`: The name of the node to be created. + fn run_container(test: F, node_id: String) { + let mut nb_retry = 5; + + loop { + match setup_node(SetupNodeRequest::new( + node_id.to_string(), + Ipv4Inet::from_str("127.0.0.1/32").unwrap(), + )) { + Ok(_) => break, + Err(e) => { + if nb_retry <= 0 { + panic!("{:?}", e) + } + clean_node(CleanNodeRequest::new(node_id.to_string())).ok(); + nb_retry -= 1; + std::thread::sleep(core::time::Duration::from_millis(1000)); + } + }; + } + + tokio_test::block_on(test); + + clean_node(CleanNodeRequest::new(node_id.to_string())).ok(); } - async fn create_container_test() -> Result<(), Error> { - let container = create_default_container().await?; + /// It creates a container, checks that it exists, and then stops it + /// + /// Arguments: + /// + /// * `node_id`: The id of the node to create the container on. + /// + /// Returns: + /// + /// A Result<(), Error> + async fn create_container_test(node_id: String) -> Result<(), Error> { + let container = create_default_container(node_id).await?; let docker = Docker::connect_with_socket_defaults()?; @@ -232,23 +319,24 @@ mod tests { .iter() .any(|cont| cont.id.as_ref().unwrap() == &container.id),); - let _ = &docker - .remove_container( - container.id().as_str(), - Some(RemoveContainerOptions { - force: true, - ..Default::default() - }), - ) - .await?; + container.stop().await.unwrap(); Ok(()) } - async fn stop_container_test() -> Result<(), Error> { + /// It creates a container, stops it, and then checks that it's no longer in the list of containers + /// + /// Arguments: + /// + /// * `node_id`: The id of the node to run the test on. + /// + /// Returns: + /// + /// A Result<(), Error> + async fn stop_container_test(node_id: String) -> Result<(), Error> { let docker = Docker::connect_with_socket_defaults()?; - let container = create_default_container().await?; + let container = create_default_container(node_id).await?; container.stop().await?; @@ -271,11 +359,13 @@ mod tests { #[test] fn test_create_container() { - tokio_test::block_on(create_container_test()).unwrap(); + let name = "test_create"; + run_container(create_container_test(name.to_string()), name.to_string()); } #[test] fn test_stop_container() { - tokio_test::block_on(stop_container_test()).unwrap(); + let name = "test_stop"; + run_container(stop_container_test(name.to_string()), name.to_string()); } } diff --git a/node-agent/workload_manager/src/workload_manager/workload/mod.rs b/node-agent/workload_manager/src/workload_manager/workload/mod.rs index 8262ddd0..4993dce7 100644 --- a/node-agent/workload_manager/src/workload_manager/workload/mod.rs +++ b/node-agent/workload_manager/src/workload_manager/workload/mod.rs @@ -1,12 +1,3 @@ -use anyhow::Result; -use proto::agent::{Instance, Type}; -use workload_trait::Workload; - mod container; +pub mod workload_runner; pub mod workload_trait; - -pub async fn create(instance: Instance) -> Result { - match instance.r#type() { - Type::Container => container::Container::new(instance).await, - } -} diff --git a/node-agent/workload_manager/src/workload_manager/workload/workload_runner.rs b/node-agent/workload_manager/src/workload_manager/workload/workload_runner.rs new file mode 100644 index 00000000..7f0c1f12 --- /dev/null +++ b/node-agent/workload_manager/src/workload_manager/workload/workload_runner.rs @@ -0,0 +1,51 @@ +use super::{container::Container, workload_trait::Workload}; +use anyhow::Result; +use proto::agent::{Instance, Type}; + +pub struct NetworkSettings { + pub node_id: String, + pub bridge_name: String, +} + +pub struct WorkloadRunner { + network_settings: NetworkSettings, +} + +impl WorkloadRunner { + /// Create a new WorkloadRunner and returns it + /// + /// Arguments: + /// + /// * `node_id`: The ID of the node that the instance is running on. + /// * `node_ip`: The IP address of the node that the instance is running on. + /// + /// Returns: + /// + /// A new instance of the WorkloadRunner struct. + pub fn new(node_id: String) -> Self { + WorkloadRunner { + network_settings: NetworkSettings { + node_id: node_id.clone(), + bridge_name: network::utils::bridge_name(node_id), + }, + } + } + + /// run a workload based on the given instance definition + /// + /// The `run` function is the entry point for the `Workload` trait. It is the function that is + /// called when a user wants to run a workload + /// + /// Arguments: + /// + /// * `instance`: Instance - The instance of the workload to run. + /// + /// Returns: + /// + /// A future that resolves to a Workload. + pub async fn run(&self, instance: Instance) -> Result { + match instance.r#type() { + Type::Container => Container::new(instance, &self.network_settings).await, + } + } +} diff --git a/node-agent/workload_manager/src/workload_manager/workload/workload_trait.rs b/node-agent/workload_manager/src/workload_manager/workload/workload_trait.rs index 78d415f0..3436f103 100644 --- a/node-agent/workload_manager/src/workload_manager/workload/workload_trait.rs +++ b/node-agent/workload_manager/src/workload_manager/workload/workload_trait.rs @@ -4,14 +4,10 @@ use anyhow::Result; pub trait Workload { fn id(&self) -> String; - // - // Gracefully stops a workload - // + /// Gracefully stops a workload async fn stop(&self) -> Result<()>; - // - // Force a workload to stop - // (equivalent to a `kill -9` on linux) - // + /// Force a workload to stop + /// (equivalent to a `kill -9` on linux) async fn kill(&self) -> Result<()>; } From 399fb4d6af3dc95e3077430e8b654dff52fdba04 Mon Sep 17 00:00:00 2001 From: Alexis Langlet Date: Tue, 30 Aug 2022 17:44:30 +0200 Subject: [PATCH 19/21] fix: run ci as root Run tests in ci as root in order to test network related things Signed-off-by: Alexis Langlet --- .github/workflows/ci.yml | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eddd648e..fd06b1b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,29 +2,32 @@ name: Kudo CI on: push: - branches: [ "main", "project-demo-202208" ] + branches: ["main", "project-demo-202208"] pull_request: - branches: [ "main", "project-demo-202208" ] + branches: ["main", "project-demo-202208"] env: CARGO_TERM_COLOR: always jobs: build: - runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Install clippy - run: rustup component add clippy - - name: Install rustfmt - run: rustup component add rustfmt - - name: Build - run: make kudo - - name: Lint - run: make lint - - name: Format - run: make format - - name: Tests - run: make check + - uses: actions/checkout@v3 + - name: Install clippy + run: rustup component add clippy + - name: Install rustfmt + run: rustup component add rustfmt + - name: Cargo setup + run: | + echo "[target.x86_64-unknown-linux-gnu]" >> ~/.cargo/config + echo "runner = 'sudo -E'" >> ~/.cargo/config + - name: Build + run: make kudo + - name: Lint + run: make lint + - name: Format + run: make format + - name: Tests + run: make check From 8ec7a83f90ee922f9cd461a74bc9c51702f429a0 Mon Sep 17 00:00:00 2001 From: esteban Date: Mon, 29 Aug 2022 19:07:01 +0200 Subject: [PATCH 20/21] feat: start node agent api implementation You can find the implementation of the node agent grpc client. The role of the client is to connect, register and finally send node status to the scheduler. Moreover, a configuration has been implemented with confy for grpc client (and server, available in the next commit). Signed-off-by: esteban --- node-agent/Cargo.toml | 15 ++- node-agent/src/config.rs | 62 +++++++++++ node-agent/src/main.rs | 215 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 288 insertions(+), 4 deletions(-) create mode 100644 node-agent/src/config.rs diff --git a/node-agent/Cargo.toml b/node-agent/Cargo.toml index 4321a7e0..4769aa95 100644 --- a/node-agent/Cargo.toml +++ b/node-agent/Cargo.toml @@ -7,5 +7,16 @@ edition = "2021" [dependencies] proto = { path = "../proto" } -workload_manager= {path = "./workload_manager"} -tokio = { version = "1.0", features = ["full"] } \ No newline at end of file +node_manager = { path = "./node_manager" } +network = { path = "../network" } +tonic = "0.7.2" +tokio = { version = "1.20.1", features = ["full"] } +tokio-stream = "0.1.9" +async-stream = "0.3.3" +env_logger = "0.9.0" +log = "0.4.0" +confy = "0.4.0" +serde = "1.0.142" +serde_derive = "1.0.142" +uuid = { version="1.1.2", features = ["v4"] } +cidr = "0.2.1" diff --git a/node-agent/src/config.rs b/node-agent/src/config.rs new file mode 100644 index 00000000..d5b23bec --- /dev/null +++ b/node-agent/src/config.rs @@ -0,0 +1,62 @@ +use serde_derive::{Deserialize, Serialize}; + +/// +/// NodeAgentConfig is a structure to define node agent configuration +/// +/// server: node agent grpc server config +/// client: scheduler grpc server config +/// +#[derive(Serialize, Deserialize, Debug)] +pub struct NodeAgentConfig { + pub server: GrpcServerConfig, + pub client: GrpcServerConfig, +} + +/// +/// GrpcServerConfig is a structure to define any grpc server configuration +/// +/// host: ip or address of the grpc server +/// port: grpc server's port +/// +#[derive(Serialize, Deserialize, Debug)] +pub struct GrpcServerConfig { + pub host: String, + pub port: u16, +} + +/// The default configuration for node agent +impl Default for NodeAgentConfig { + fn default() -> Self { + NodeAgentConfig { + server: GrpcServerConfig { + host: "127.0.0.1".to_string(), + port: 50053, + }, + client: GrpcServerConfig { + host: "127.0.0.1".to_string(), + port: 50052, + }, + } + } +} + +/// new function: allows you to define custom server and client configurations +impl NodeAgentConfig { + pub fn new( + server_host: String, + server_port: u16, + client_host: String, + client_port: u16, + ) -> Self { + Self { + server: GrpcServerConfig { + host: server_host, + port: server_port, + }, + client: GrpcServerConfig { + host: client_host, + port: client_port, + }, + } + } +} diff --git a/node-agent/src/main.rs b/node-agent/src/main.rs index e7a11a96..d2e1df38 100644 --- a/node-agent/src/main.rs +++ b/node-agent/src/main.rs @@ -1,3 +1,214 @@ -fn main() { - println!("Hello, world!"); +use std::env; +use std::net::Ipv4Addr; +use std::str::FromStr; +use std::sync::{Arc, Mutex}; +use std::thread::sleep; +use std::time::Duration; + +use cidr::Ipv4Inet; +use log::{debug, info, trace}; +use network::node::request::SetupNodeRequest; +use tokio::time; +use tonic::{transport::Server, Request, Response, Status}; +use uuid::Uuid; + +mod config; + +use config::{GrpcServerConfig, NodeAgentConfig}; +use network::node::setup_node; +use node_manager::NodeSystem; + +use proto::scheduler::{ + node_service_client::NodeServiceClient, NodeRegisterRequest, NodeRegisterResponse, NodeStatus, + Resource, ResourceSummary, Status as SchedulerStatus, +}; + +const NUMBER_OF_CONNECTION_ATTEMPTS: u16 = 10; +/// +/// This function allows you to connect to the scheduler's grpc server. +/// +async fn connect_to_scheduler( + addr: String, +) -> Option> { + NodeServiceClient::connect(addr.clone()).await.ok() +} + +/// +/// This function allows you to register to the scheduler's grpc server. +/// +async fn register_to_scheduler( + client: &mut NodeServiceClient, + certificate: String, +) -> Option> { + let register_request = tonic::Request::new(NodeRegisterRequest { certificate }); + + client.register(register_request).await.ok() +} + +/// +/// This function allows you to send node status to the scheduler's grpc server. +/// +async fn send_node_satus_to_scheduler( + client: &mut NodeServiceClient, + node_system_arc: Arc>, + node_id: String, +) -> Option> { + let node_status_stream = async_stream::stream! { + let mut interval = time::interval(Duration::from_secs(1)); + + loop { + interval.tick().await; + + let cpu_limit = node_system_arc.lock().unwrap().total_cpu(); + let cpu_usage = node_system_arc.lock().unwrap().used_cpu(); + + let memory_limit = node_system_arc.lock().unwrap().total_memory(); + let memory_usage = node_system_arc.lock().unwrap().used_memory(); + + let disk_limit = node_system_arc.lock().unwrap().total_disk(); + let disk_usage = node_system_arc.lock().unwrap().used_disk(); + + let node_status = NodeStatus { + id: node_id.clone(), + status: SchedulerStatus::Running as i32, + status_description: "".into(), + resource: Some(Resource { + limit: Some(ResourceSummary { + cpu: cpu_limit, + memory: memory_limit, + disk: disk_limit, + }), + usage: Some(ResourceSummary { + cpu: cpu_usage, + memory: memory_usage, + disk: disk_usage, + }), + }), + }; + + debug!("Node resources sent to the Scheduler"); + + yield node_status; + } + }; + + client.status(Request::new(node_status_stream)).await.ok() +} + +/// +/// This function launch the Node Agent grpc client. +/// First, the client registered to the Scheduler. +/// Secondaly, once connected to it, it's send node resources to the Scheduler. +/// +fn create_grpc_client(config: GrpcServerConfig, node_id: String) -> tokio::task::JoinHandle<()> { + tokio::spawn(async move { + // Connection to the Scheduler's grpc server + + let addr = format!("http://{}:{}", config.host, config.port); + let mut connection = connect_to_scheduler(addr.clone()).await; + + let mut attempts: u16 = 0; + while connection.is_none() { + if attempts <= NUMBER_OF_CONNECTION_ATTEMPTS { + sleep(Duration::from_secs(1)); + + debug!("Connection to grpc scheduler server failed, retrying..."); + connection = connect_to_scheduler(addr.clone()).await; + + attempts += 1; + } else { + panic!("Error, unable to connect to the Scheduler server."); + } + } + + let mut client = connection.unwrap(); + + info!("Node agent connected to the Scheduler at {}", addr); + + // Registration with the Scheduler + + let certificate = node_id.clone(); + let mut registration = register_to_scheduler(&mut client, certificate).await; + + // setup node + + let node_ip = registration.unwrap().into_inner().id; + let node_ip_addr = Ipv4Addr::from_str(&node_ip).unwrap(); + let node_ip_cidr = Ipv4Inet::new(node_ip_addr, 24).unwrap(); + + let request = SetupNodeRequest::new(node_id.to_string(), node_ip_cidr); + let response = setup_node(request).unwrap(); + + attempts = 0; + while registration.is_none() { + if attempts <= NUMBER_OF_CONNECTION_ATTEMPTS { + sleep(Duration::from_secs(1)); + + debug!("Registration to the Scheduler failed, retrying..."); + registration = register_to_scheduler(&mut client, certificate).await; + + attempts += 1; + } else { + panic!("Error, unable to register to the Scheduler."); + } + } + + info!("Node agent registered to the Scheduler"); + + // Send Node status to the Scheduler + + let node_system = NodeSystem::new(); + let arc_node_system = Arc::new(Mutex::new(node_system)); + + let mut send_node_status_to_scheduler = + send_node_satus_to_scheduler(&mut client, Arc::clone(&arc_node_system), node_id).await; + + attempts = 0; + while send_node_status_to_scheduler.is_none() { + if attempts <= NUMBER_OF_CONNECTION_ATTEMPTS { + sleep(Duration::from_secs(1)); + + debug!("Sending node status to the Scheduler failed, retrying..."); + send_node_status_to_scheduler = send_node_satus_to_scheduler( + &mut client, + Arc::clone(&arc_node_system), + node_id, + ) + .await; + + attempts += 1; + } else { + panic!("Error, unable to send node status to the Scheduler."); + } + } + }) +} +#[tokio::main] +async fn main() -> Result<(), Box> { + env_logger::init(); + + info!("Starting up node agent"); + + info!("Loading config"); + let mut dir = env::current_exe()?; // get executable path + dir.pop(); // remove executable name + dir.push("agent.conf"); // add config file name + + trace!("Node Agent config at: {:?}", dir); + + // load config from path + let config: NodeAgentConfig = confy::load_path(dir.as_path())?; + debug!("config: {:?}", config); + + // generate node id + let node_id = Uuid::new_v4().to_string(); + + // start grpc server and client + let client_handler = create_grpc_client(config.client, node_id.clone()); + + client_handler.await?; + + info!("Shutting down node agent"); + + Ok(()) } From 2229e617c91c39b5c1a83250f4204ed6cb90c7e9 Mon Sep 17 00:00:00 2001 From: esteban Date: Mon, 29 Aug 2022 19:10:43 +0200 Subject: [PATCH 21/21] feat: implement grpc server in node agent In this commit, you can find the implementation of the node agent grpc server. This server handle scheduler request by using the workload_manager library (node agent internal crate). Signed-off-by: esteban --- Cargo.lock | 69 +++++++++++--------- node-agent/Cargo.toml | 1 + node-agent/src/main.rs | 140 +++++++++++++++++++++++++++++++++++------ 3 files changed, 162 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 407c7f37..040a166b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,9 +224,9 @@ dependencies = [ [[package]] name = "android_system_properties" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ed72e1635e121ca3e79420540282af22da58be50de153d36f81ddc6b83aa9e" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] @@ -474,9 +474,9 @@ checksum = "300bccc729b1ada84523246038aad61fead689ac362bb9d44beea6f6a188c34b" [[package]] name = "clap" -version = "3.2.17" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29e724a68d9319343bb3328c9cc2dfde263f4b3142ee1059a9980580171c954b" +checksum = "b15f2ea93df33549dbe2e8eecd1ca55269d63ae0b3ba1f55db030817d1c2867f" dependencies = [ "atty", "bitflags", @@ -491,9 +491,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.17" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13547f7012c01ab4a0e8f8967730ada8f9fdf419e8b6c792788f39cf4e46eefa" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck", "proc-macro-error", @@ -896,9 +896,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab30e97ab6aacfe635fad58f22c2bb06c8b685f7421eb1e064a729e2a5f481fa" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" dependencies = [ "futures-channel", "futures-core", @@ -911,9 +911,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bfc52cbddcfd745bf1740338492bb0bd83d76c67b445f91c5fb29fae29ecaa1" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" dependencies = [ "futures-core", "futures-sink", @@ -921,15 +921,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2acedae88d38235936c3922476b10fced7b2b68136f5e3c03c2d5be348a1115" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" [[package]] name = "futures-executor" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d11aa21b5b587a64682c0094c2bdd4df0076c5324961a40cc3abd7f37930528" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" dependencies = [ "futures-core", "futures-task", @@ -938,15 +938,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93a66fc6d035a26a3ae255a6d2bca35eda63ae4c5512bef54449113f7a1228e5" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" [[package]] name = "futures-macro" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0db9cce532b0eae2ccf2766ab246f114b56b9cf6d445e00c2549fbc100ca045d" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" dependencies = [ "proc-macro2", "quote", @@ -955,21 +955,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca0bae1fe9752cf7fd9b0064c674ae63f97b37bc714d745cbde0afb7ec4e6765" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" [[package]] name = "futures-task" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "842fc63b931f4056a24d59de13fb1272134ce261816e063e634ad0c15cdc5306" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" [[package]] name = "futures-util" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0828a5471e340229c11c77ca80017937ce3c58cb788a17e5f1c2d5c485a9577" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" dependencies = [ "futures-channel", "futures-core", @@ -1169,13 +1169,14 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.46" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad2bfd338099682614d3ee3fe0cd72e0b6a41ca6a87f6a74a3bd593c91650501" +checksum = "4c495f162af0bf17656d0014a0eded5f3cd2f365fdd204548c2869db89359dc7" dependencies = [ "android_system_properties", "core-foundation-sys", "js-sys", + "once_cell", "wasm-bindgen", "winapi", ] @@ -1424,8 +1425,20 @@ dependencies = [ name = "node-agent" version = "0.1.0" dependencies = [ + "async-stream", + "cidr", + "confy", + "env_logger 0.9.0", + "log", + "network", + "node_manager", "proto", + "serde", + "serde_derive", "tokio", + "tokio-stream", + "tonic", + "uuid", "workload_manager", ] @@ -2047,9 +2060,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77f4e7f65455545c2153c1253d25056825e77ee2533f0e41deb65a93a34852f" +checksum = "5cf2781a4ca844dd4f9b608a1791eea19830df0ad3cdd9988cd05f1c66ccb63a" dependencies = [ "cfg-if 1.0.0", "cpufeatures", diff --git a/node-agent/Cargo.toml b/node-agent/Cargo.toml index 4769aa95..b2373175 100644 --- a/node-agent/Cargo.toml +++ b/node-agent/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] proto = { path = "../proto" } +workload_manager = { path = "./workload_manager" } node_manager = { path = "./node_manager" } network = { path = "../network" } tonic = "0.7.2" diff --git a/node-agent/src/main.rs b/node-agent/src/main.rs index d2e1df38..b9395dea 100644 --- a/node-agent/src/main.rs +++ b/node-agent/src/main.rs @@ -1,14 +1,17 @@ use std::env; use std::net::Ipv4Addr; use std::str::FromStr; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::thread::sleep; use std::time::Duration; use cidr::Ipv4Inet; use log::{debug, info, trace}; use network::node::request::SetupNodeRequest; +use tokio::sync::mpsc::channel; +use tokio::sync::Mutex; use tokio::time; +use tokio_stream::wrappers::ReceiverStream; use tonic::{transport::Server, Request, Response, Status}; use uuid::Uuid; @@ -17,13 +20,104 @@ mod config; use config::{GrpcServerConfig, NodeAgentConfig}; use network::node::setup_node; use node_manager::NodeSystem; +use workload_manager::workload_manager::WorkloadManager; +use proto::agent::{ + instance_service_server::InstanceService, instance_service_server::InstanceServiceServer, + Instance, InstanceStatus, SignalInstruction, +}; use proto::scheduler::{ node_service_client::NodeServiceClient, NodeRegisterRequest, NodeRegisterResponse, NodeStatus, Resource, ResourceSummary, Status as SchedulerStatus, }; const NUMBER_OF_CONNECTION_ATTEMPTS: u16 = 10; + +/// +/// This Struct implement the Instance service from Node Agent proto file +/// +pub struct InstanceServiceController { + workload_manager: Arc>, +} + +impl InstanceServiceController { + pub fn new(node_id: String) -> Self { + Self { + workload_manager: Arc::new(Mutex::new(WorkloadManager::new(node_id))), + } + } +} + +#[tonic::async_trait] +impl InstanceService for InstanceServiceController { + type createStream = ReceiverStream>; + + async fn create( + &self, + request: Request, + ) -> Result, Status> { + let instance = request.into_inner(); + let channel = channel(1024); + + // call workload_manager create function in an other thread + let workload_manager = self.workload_manager.clone(); + + tokio::spawn(async move { + workload_manager + .clone() + .lock() + .await + .create(instance, channel.0.clone()) + .await + .map_err(|err| Status::internal(format!("Workload creation error: {}", err))) + .ok(); + }); + + // send receiver to scheduler + Ok(Response::new(ReceiverStream::new(channel.1))) + } + + async fn signal(&self, request: Request) -> Result, Status> { + let signal_instruction = request.into_inner(); + + // call workload_manager signal function in an other thread + let workload_manager = self.workload_manager.clone(); + + tokio::spawn(async move { + workload_manager + .clone() + .lock() + .await + .signal(signal_instruction) + .await + .map_err(|_| Status::internal("Cannot send signal to the workload")) + .ok(); + }); + + Ok(Response::new(())) + } +} + +/// +/// This function starts the grpc server of the Node Agent. +/// The server listens and responds to requests from the Scheduler. +/// The default port is 50053. +/// +fn create_grpc_server(config: GrpcServerConfig, node_id: String) -> tokio::task::JoinHandle<()> { + let addr = format!("{}:{}", config.host, config.port).parse().unwrap(); + let instance_service_controller = InstanceServiceController::new(node_id); + + info!("Node Agent server listening on {}", addr); + + tokio::spawn(async move { + Server::builder() + .add_service(InstanceServiceServer::new(instance_service_controller)) + .serve(addr) + .await + .unwrap() + }) +} + /// /// This function allows you to connect to the scheduler's grpc server. /// @@ -48,25 +142,24 @@ async fn register_to_scheduler( /// /// This function allows you to send node status to the scheduler's grpc server. /// -async fn send_node_satus_to_scheduler( +async fn send_node_status_to_scheduler( client: &mut NodeServiceClient, - node_system_arc: Arc>, + node_system_arc: Arc>, node_id: String, ) -> Option> { let node_status_stream = async_stream::stream! { let mut interval = time::interval(Duration::from_secs(1)); + let cpu_limit = node_system_arc.lock().await.total_cpu(); + let memory_limit = node_system_arc.lock().await.total_memory(); + let disk_limit = node_system_arc.lock().await.total_disk(); + loop { interval.tick().await; - let cpu_limit = node_system_arc.lock().unwrap().total_cpu(); - let cpu_usage = node_system_arc.lock().unwrap().used_cpu(); - - let memory_limit = node_system_arc.lock().unwrap().total_memory(); - let memory_usage = node_system_arc.lock().unwrap().used_memory(); - - let disk_limit = node_system_arc.lock().unwrap().total_disk(); - let disk_usage = node_system_arc.lock().unwrap().used_disk(); + let cpu_usage = node_system_arc.lock().await.used_cpu(); + let memory_usage = node_system_arc.lock().await.used_memory(); + let disk_usage = node_system_arc.lock().await.used_disk(); let node_status = NodeStatus { id: node_id.clone(), @@ -128,11 +221,11 @@ fn create_grpc_client(config: GrpcServerConfig, node_id: String) -> tokio::task: // Registration with the Scheduler let certificate = node_id.clone(); - let mut registration = register_to_scheduler(&mut client, certificate).await; + let mut registration = register_to_scheduler(&mut client, certificate.clone()).await; - // setup node + // setup node network - let node_ip = registration.unwrap().into_inner().id; + let node_ip = registration.unwrap().into_inner().ip; let node_ip_addr = Ipv4Addr::from_str(&node_ip).unwrap(); let node_ip_cidr = Ipv4Inet::new(node_ip_addr, 24).unwrap(); @@ -145,7 +238,7 @@ fn create_grpc_client(config: GrpcServerConfig, node_id: String) -> tokio::task: sleep(Duration::from_secs(1)); debug!("Registration to the Scheduler failed, retrying..."); - registration = register_to_scheduler(&mut client, certificate).await; + registration = register_to_scheduler(&mut client, certificate.clone()).await; attempts += 1; } else { @@ -160,19 +253,23 @@ fn create_grpc_client(config: GrpcServerConfig, node_id: String) -> tokio::task: let node_system = NodeSystem::new(); let arc_node_system = Arc::new(Mutex::new(node_system)); - let mut send_node_status_to_scheduler = - send_node_satus_to_scheduler(&mut client, Arc::clone(&arc_node_system), node_id).await; + let mut send_node_resources_to_scheduler = send_node_status_to_scheduler( + &mut client, + Arc::clone(&arc_node_system), + node_id.clone(), + ) + .await; attempts = 0; - while send_node_status_to_scheduler.is_none() { + while send_node_resources_to_scheduler.is_none() { if attempts <= NUMBER_OF_CONNECTION_ATTEMPTS { sleep(Duration::from_secs(1)); debug!("Sending node status to the Scheduler failed, retrying..."); - send_node_status_to_scheduler = send_node_satus_to_scheduler( + send_node_resources_to_scheduler = send_node_status_to_scheduler( &mut client, Arc::clone(&arc_node_system), - node_id, + node_id.clone(), ) .await; @@ -183,6 +280,7 @@ fn create_grpc_client(config: GrpcServerConfig, node_id: String) -> tokio::task: } }) } + #[tokio::main] async fn main() -> Result<(), Box> { env_logger::init(); @@ -205,8 +303,10 @@ async fn main() -> Result<(), Box> { // start grpc server and client let client_handler = create_grpc_client(config.client, node_id.clone()); + let server_handler = create_grpc_server(config.server, node_id.clone()); client_handler.await?; + server_handler.await?; info!("Shutting down node agent");