From 3cb2a7f808c445406ab042b2178c084cf7af9492 Mon Sep 17 00:00:00 2001 From: liyuanr Date: Tue, 9 Jan 2024 20:13:54 +0800 Subject: [PATCH 1/2] proxy: add unit test and mock files Add the proxy unit test and the apiserver mock file used for the proxy unit test. Signed-off-by: liyuanr --- KubeOS-Rust/Cargo.lock | 67 ++ KubeOS-Rust/proxy/Cargo.toml | 8 + .../proxy/src/controller/agentclient.rs | 15 +- .../proxy/src/controller/apiserver_mock.rs | 607 ++++++++++++++++++ .../proxy/src/controller/controller.rs | 105 ++- KubeOS-Rust/proxy/src/controller/mod.rs | 2 + 6 files changed, 801 insertions(+), 3 deletions(-) create mode 100644 KubeOS-Rust/proxy/src/controller/apiserver_mock.rs diff --git a/KubeOS-Rust/Cargo.lock b/KubeOS-Rust/Cargo.lock index d9300f60..19bdc0c7 100644 --- a/KubeOS-Rust/Cargo.lock +++ b/KubeOS-Rust/Cargo.lock @@ -42,6 +42,28 @@ dependencies = [ "serde_json", ] +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + [[package]] name = "async-trait" version = "0.1.74" @@ -1168,6 +1190,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "mockall_double" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1ca96e5ac35256ae3e13536edd39b172b88f41615e1d7b653c8ad24524113e8" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.37", +] + [[package]] name = "mockito" version = "0.31.1" @@ -1478,16 +1512,21 @@ name = "proxy" version = "0.1.0" dependencies = [ "anyhow", + "assert-json-diff", "async-trait", "chrono", "cli", "env_logger", "futures", "h2", + "http", + "hyper", "k8s-openapi", "kube", "log", "manager", + "mockall", + "mockall_double", "regex", "reqwest", "schemars", @@ -1498,6 +1537,7 @@ dependencies = [ "thread_local", "tokio", "tokio-retry", + "tower-test", ] [[package]] @@ -2142,6 +2182,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-test" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719" +dependencies = [ + "async-stream", + "bytes", + "futures-core", + "tokio", + "tokio-stream", +] + [[package]] name = "tokio-util" version = "0.6.10" @@ -2229,6 +2282,20 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +[[package]] +name = "tower-test" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4546773ffeab9e4ea02b8872faa49bb616a80a7da66afc2f32688943f97efa7" +dependencies = [ + "futures-util", + "pin-project", + "tokio", + "tokio-test", + "tower-layer", + "tower-service", +] + [[package]] name = "tracing" version = "0.1.35" diff --git a/KubeOS-Rust/proxy/Cargo.toml b/KubeOS-Rust/proxy/Cargo.toml index b96bd1cd..ab98d283 100644 --- a/KubeOS-Rust/proxy/Cargo.toml +++ b/KubeOS-Rust/proxy/Cargo.toml @@ -31,3 +31,11 @@ thiserror = "1.0.29" thread_local = "=1.1.4" tokio = { version = "=1.14.0", features = ["macros", "rt-multi-thread"] } tokio-retry = "0.3" + +[dev-dependencies] +assert-json-diff = "2.0.2" +http = "0.2.9" +hyper = "0.14.25" +tower-test = "0.4.0" +mockall = { version = "=0.11.3" } +mockall_double = "0.3.0" diff --git a/KubeOS-Rust/proxy/src/controller/agentclient.rs b/KubeOS-Rust/proxy/src/controller/agentclient.rs index ddc4f290..cc3ab079 100644 --- a/KubeOS-Rust/proxy/src/controller/agentclient.rs +++ b/KubeOS-Rust/proxy/src/controller/agentclient.rs @@ -12,7 +12,6 @@ use std::{collections::HashMap, path::Path}; -use agent_call::AgentCallClient; use agent_error::Error; use cli::{ client::Client, @@ -23,6 +22,13 @@ use cli::{ }; use manager::api::{CertsInfo, ConfigureRequest, KeyInfo as AgentKeyInfo, Sysconfig as AgentSysconfig, UpgradeRequest}; +#[cfg(test)] +use mockall::automock; +#[cfg(test)] +use mockall_double::double; +#[cfg_attr(test, double)] +use agent_call::AgentCallClient; + pub struct UpgradeInfo { pub version: String, pub image_type: String, @@ -45,6 +51,7 @@ pub struct KeyInfo { pub operation: String, } +#[cfg_attr(test, automock)] pub trait AgentMethod { fn prepare_upgrade_method(&self, upgrade_info: UpgradeInfo, agent_call: AgentCallClient) -> Result<(), Error>; fn upgrade_method(&self, agent_call: AgentCallClient) -> Result<(), Error>; @@ -54,9 +61,13 @@ pub trait AgentMethod { pub mod agent_call { use super::{Client, Error, RpcMethod}; + #[cfg(test)] + use mockall::automock; + #[derive(Default)] pub struct AgentCallClient {} - + + #[cfg_attr(test, automock)] impl AgentCallClient { pub fn call_agent(&self, client: &Client, method: T) -> Result<(), Error> { match method.call(client) { diff --git a/KubeOS-Rust/proxy/src/controller/apiserver_mock.rs b/KubeOS-Rust/proxy/src/controller/apiserver_mock.rs new file mode 100644 index 00000000..a7ae647a --- /dev/null +++ b/KubeOS-Rust/proxy/src/controller/apiserver_mock.rs @@ -0,0 +1,607 @@ +use self::mock_error::Error; +use crate::controller::{ + apiclient::{ApplyApi, ControllerClient}, + crd::{OSInstance, OSInstanceSpec, OSSpec, OS}, + values::{LABEL_OSINSTANCE, NODE_STATUS_IDLE,LABEL_UPGRADING}, + ProxyController, + +}; +use super::{agentclient::*, values::{NODE_STATUS_UPGRADE, NODE_STATUS_CONFIG}, crd::{Configs, OSInstanceStatus}}; +use anyhow::Result; +use http::{Request, Response}; +use hyper::{body::to_bytes, Body}; +use kube::{api::ObjectMeta, core::{ObjectList, ListMeta}}; +use kube::{Client, Resource, ResourceExt}; +use std::collections::BTreeMap; +use k8s_openapi::api::core::v1::{Node, NodeStatus, NodeSystemInfo, NodeSpec}; +use k8s_openapi::api::core::v1::Pod; + + +type ApiServerHandle = tower_test::mock::Handle, Response>; +pub struct ApiServerVerifier(ApiServerHandle); + +pub enum Testcases { + OSInstanceNotExist(OSInstance), + UpgradeNormal(OSInstance), + UpgradeUpgradeconfigsVersionMismatch(OSInstance), + UpgradeOSInstaceNodestatusConfig(OSInstance), + UpgradeOSInstaceNodestatusIdle(OSInstance), + ConfigNormal(OSInstance), + ConfigVersionMismatchReassign(OSInstance), + ConfigVersionMismatchUpdate(OSInstance), +} + +pub async fn timeout_after_5s(handle: tokio::task::JoinHandle<()>) { + tokio::time::timeout(std::time::Duration::from_secs(5), handle) + .await + .expect("timeout on mock apiserver") + .expect("scenario succeeded") +} + +impl ApiServerVerifier { + pub fn run(self, cases: Testcases) -> tokio::task::JoinHandle<()> { + tokio::spawn(async move { + match cases { + Testcases::OSInstanceNotExist(osi) => { + self.handler_osinstance_get_not_exist(osi.clone()).await.unwrap() + .handler_osinstance_creation(osi.clone()).await.unwrap() + .handler_osinstance_get_exist(osi.clone()).await.unwrap() + .handler_node_get(osi).await + }, + Testcases::UpgradeNormal(osi) => { + self.handler_osinstance_get_exist(osi.clone()) + .await.unwrap().handler_osinstance_get_exist(osi.clone()) + .await.unwrap().handler_node_get_with_label(osi.clone()) + .await.unwrap().handler_osinstance_patch_upgradeconfig_v2(osi.clone()) + .await.unwrap().handler_node_cordon(osi.clone()) + .await.unwrap().handler_node_pod_list_get(osi).await + }, + Testcases::UpgradeUpgradeconfigsVersionMismatch(osi) => { + self.handler_osinstance_get_exist(osi.clone()) + .await.unwrap().handler_osinstance_get_exist(osi.clone()) + .await.unwrap().handler_node_get_with_label(osi.clone()) + .await.unwrap().handler_node_update_delete_label(osi.clone()) + .await.unwrap().handler_node_uncordon(osi.clone()) + .await.unwrap().handler_osinstance_patch_nodestatus_idle(osi) + .await + }, + Testcases::UpgradeOSInstaceNodestatusConfig(osi) => { + self.handler_osinstance_get_exist(osi.clone()) + .await.unwrap().handler_osinstance_get_exist(osi.clone()) + .await.unwrap().handler_node_get_with_label(osi.clone()) + .await + }, + Testcases::UpgradeOSInstaceNodestatusIdle(osi) => { + self.handler_osinstance_get_exist(osi.clone()) + .await.unwrap().handler_osinstance_get_exist(osi.clone()) + .await.unwrap().handler_node_get_with_label(osi.clone()) + .await.unwrap().handler_node_update_delete_label(osi.clone()) + .await.unwrap().handler_node_uncordon(osi) + .await + }, + Testcases::ConfigNormal(osi) => { + self.handler_osinstance_get_exist(osi.clone()) + .await.unwrap().handler_osinstance_get_exist(osi.clone()) + .await.unwrap().handler_node_get(osi.clone()) + .await.unwrap().handler_osinstance_patch_sysconfig_v2(osi.clone()) + .await.unwrap().handler_osinstance_patch_nodestatus_idle(osi) + .await + }, + Testcases::ConfigVersionMismatchReassign(osi) => { + self.handler_osinstance_get_exist(osi.clone()) + .await.unwrap().handler_osinstance_get_exist(osi.clone()) + .await.unwrap().handler_node_get(osi.clone()) + .await.unwrap().handler_osinstance_patch_nodestatus_idle(osi) + .await + }, + Testcases::ConfigVersionMismatchUpdate(osi) => { + self.handler_osinstance_get_exist(osi.clone()) + .await.unwrap().handler_osinstance_get_exist(osi.clone()) + .await.unwrap().handler_node_get(osi.clone()) + .await.unwrap().handler_osinstance_patch_spec_sysconfig_v2(osi) + .await + }, + + + + }.expect("Case completed without errors"); + }) + } + + async fn handler_osinstance_get_not_exist( + mut self, + osinstance: OSInstance, + ) -> Result { + let (request, send) = self.0.next_request().await.expect("service not called"); + assert_eq!(request.method(), http::Method::GET); + assert_eq!( + request.uri().to_string(), + format!( + "/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}", + osinstance.name() + ) + ); + let response_json = serde_json::json!( + { "status": "Failure", "message": "osinstances.upgrade.openeuler.org \"openeuler\" not found", "reason": "NotFound", "code": 404 } + ); + dbg!("handler_osinstance_get_not_exist"); + let response = serde_json::to_vec(&response_json).unwrap(); + send.send_response( + Response::builder() + .status(404) + .body(Body::from(response)) + .unwrap(), + ); + Ok(self) + } + async fn handler_osinstance_get_exist(mut self, osinstance: OSInstance) -> Result { + let (request, send) = self.0.next_request().await.expect("service not called"); + assert_eq!(request.method(), http::Method::GET); + assert_eq!( + request.uri().to_string(), + format!( + "/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}", + osinstance.name() + ) + ); + dbg!("handler_osinstance_get_exist"); + let response = serde_json::to_vec(&osinstance).unwrap(); + send.send_response(Response::builder().body(Body::from(response)).unwrap()); + Ok(self) + } + async fn handler_osinstance_creation(mut self, osinstance: OSInstance) -> Result { + let (request, send) = self.0.next_request().await.expect("service not called"); + assert_eq!(request.method(), http::Method::POST); + assert_eq!( + request.uri().to_string(), + format!("/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances?") + ); + dbg!("handler_osinstance_creation"); + let response = serde_json::to_vec(&osinstance).unwrap(); + send.send_response(Response::builder().body(Body::from(response)).unwrap()); + Ok(self) + } + + async fn handler_osinstance_patch_nodestatus_idle(mut self, mut osinstance: OSInstance) -> Result { + let (request, send) = self.0.next_request().await.expect("service not called"); + assert_eq!(request.method(), http::Method::PATCH); + assert_eq!( + request.uri().to_string(), + format!("/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}?",osinstance.name()) + ); + + let req_body = to_bytes(request.into_body()).await.unwrap(); + let body_json:serde_json::Value= + serde_json::from_slice(&req_body).expect("valid document from runtime"); + let spec_json = body_json.get("spec").expect("spec object").clone(); + let spec:OSInstanceSpec = serde_json::from_value(spec_json).expect("valid spec"); + assert_eq!(spec.nodestatus.clone(), NODE_STATUS_IDLE.to_string()); + + dbg!("handler_osinstance_patch_nodestatus_idle"); + osinstance.spec.nodestatus = NODE_STATUS_IDLE.to_string(); + let response = serde_json::to_vec(&osinstance).unwrap(); + send.send_response(Response::builder().body(Body::from(response)).unwrap()); + Ok(self) + } + + async fn handler_osinstance_patch_upgradeconfig_v2(mut self, mut osinstance: OSInstance) -> Result { + let (request, send) = self.0.next_request().await.expect("service not called"); + assert_eq!(request.method(), http::Method::PATCH); + assert_eq!( + request.uri().to_string(), + format!("/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}/status?",osinstance.name()) + ); + + let req_body = to_bytes(request.into_body()).await.unwrap(); + let body_json:serde_json::Value= + serde_json::from_slice(&req_body).expect("valid document from runtime"); + let status_json = body_json.get("status").expect("status object").clone(); + let status:OSInstanceStatus = serde_json::from_value(status_json).expect("valid status"); + + assert_eq!(status.upgradeconfigs.expect("upgradeconfigs is not None").clone(), + osinstance.spec.clone().upgradeconfigs.expect("upgradeconfig is not None")); + + osinstance.status.as_mut().unwrap().upgradeconfigs = osinstance.spec.upgradeconfigs.clone(); + + dbg!("handler_osinstance_patch_upgradeconfig_v2"); + let response = serde_json::to_vec(&osinstance).unwrap(); + send.send_response(Response::builder().body(Body::from(response)).unwrap()); + Ok(self) + } + + async fn handler_osinstance_patch_sysconfig_v2(mut self, mut osinstance: OSInstance) -> Result { + let (request, send) = self.0.next_request().await.expect("service not called"); + assert_eq!(request.method(), http::Method::PATCH); + assert_eq!( + request.uri().to_string(), + format!("/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}/status?",osinstance.name()) + ); + + let req_body = to_bytes(request.into_body()).await.unwrap(); + let body_json:serde_json::Value= + serde_json::from_slice(&req_body).expect("valid osinstance"); + let status_json = body_json.get("status").expect("status object").clone(); + let status:OSInstanceStatus = serde_json::from_value(status_json).expect("valid status"); + + assert_eq!(status.sysconfigs.expect("sysconfigs is not None").clone(), + osinstance.spec.clone().sysconfigs.expect("sysconfig is not None")); + + osinstance.status.as_mut().unwrap().sysconfigs = osinstance.spec.sysconfigs.clone(); + + dbg!("handler_osinstance_patch_sysconfig_v2"); + let response = serde_json::to_vec(&osinstance).unwrap(); + send.send_response(Response::builder().body(Body::from(response)).unwrap()); + Ok(self) + } + + async fn handler_osinstance_patch_spec_sysconfig_v2(mut self, mut osinstance: OSInstance) -> Result { + let (request, send) = self.0.next_request().await.expect("service not called"); + assert_eq!(request.method(), http::Method::PATCH); + assert_eq!( + request.uri().to_string(), + format!("/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}?",osinstance.name()) + ); + + let req_body = to_bytes(request.into_body()).await.unwrap(); + let body_json:serde_json::Value= + serde_json::from_slice(&req_body).expect("valid osinstance"); + let spec_json = body_json.get("spec").expect("spec object").clone(); + let spec:OSInstanceSpec = serde_json::from_value(spec_json).expect("valid spec"); + + assert_eq!(spec.sysconfigs.expect("upgradeconfigs is not None").clone().version.clone().unwrap(), + String::from("v2")); + + osinstance.spec.sysconfigs.as_mut().unwrap().version = Some(String::from("v2")); + + dbg!("handler_osinstance_patch_spec_sysconfig_v2"); + let response = serde_json::to_vec(&osinstance).unwrap(); + send.send_response(Response::builder().body(Body::from(response)).unwrap()); + Ok(self) + } + + + async fn handler_node_get(mut self,osinstance: OSInstance) -> Result{ + // return node with name = openeuler, osimage = KubeOS v1,no upgrade label + let (request, send) = self.0.next_request().await.expect("service not called"); + assert_eq!(request.method(), http::Method::GET); + assert_eq!( + request.uri().to_string(), + format!( + "/api/v1/nodes/{}", + osinstance.name() + ) + ); + let node = Node{ + metadata:ObjectMeta { name:Some(String::from("openeuler")), + ..Default::default()}, + spec:None, + status:Some(NodeStatus{ + node_info:Some(NodeSystemInfo{ + os_image: String::from("KubeOS v1"), + ..Default::default() + }), + ..Default::default() + }) + }; + assert_eq!(node.name(),String::from("openeuler")); + assert_eq!(node.status.as_ref().unwrap().node_info.as_ref().unwrap().os_image,String::from("KubeOS v1")); + dbg!("handler_node_get"); + let response = serde_json::to_vec(&node.clone()).unwrap(); + send.send_response(Response::builder().body(Body::from(response)).unwrap()); + Ok(self) + } + + async fn handler_node_get_with_label(mut self,osinstance: OSInstance) -> Result{ + // return node with name = openeuler, osimage = KubeOS v1,has upgrade label + let (request, send) = self.0.next_request().await.expect("service not called"); + assert_eq!(request.method(), http::Method::GET); + assert_eq!( + request.uri().to_string(), + format!( + "/api/v1/nodes/{}", + osinstance.name() + ) + ); + let mut node = Node{ + metadata:ObjectMeta { name:Some(String::from("openeuler")), + ..Default::default()}, + spec:None, + status:Some(NodeStatus{ + node_info:Some(NodeSystemInfo{ + os_image: String::from("KubeOS v1"), + ..Default::default() + }), + ..Default::default() + }) + }; + let node_labels = node.labels_mut(); + node_labels.insert(LABEL_UPGRADING.to_string(), "".to_string()); + assert_eq!(node.name(),String::from("openeuler")); + assert_eq!(node.status.as_ref().unwrap().node_info.as_ref().unwrap().os_image,String::from("KubeOS v1")); + assert!(node.labels().contains_key(LABEL_UPGRADING)); + dbg!("handler_node_get_with_label"); + let response = serde_json::to_vec(&node.clone()).unwrap(); + send.send_response(Response::builder().body(Body::from(response)).unwrap()); + Ok(self) + } + + async fn handler_node_update_delete_label(mut self,osinstance: OSInstance) -> Result{ + // return node with name = openeuler, osimage = KubeOS v1,no upgrade label + let (request, send) = self.0.next_request().await.expect("service not called"); + assert_eq!(request.method(), http::Method::PUT); + assert_eq!( + request.uri().to_string(), + format!( + "/api/v1/nodes/{}?", + osinstance.name() + ) + ); + // check request body has upgrade label + let node = Node{ + metadata:ObjectMeta { name:Some(String::from("openeuler")), + ..Default::default()}, + spec:Some(NodeSpec{ + unschedulable:Some(true), + ..Default::default() + }), + status:Some(NodeStatus{ + node_info:Some(NodeSystemInfo{ + os_image: String::from("KubeOS v1"), + ..Default::default() + }), + ..Default::default() + }) + }; + dbg!("handler_node_update_delete_label"); + let response = serde_json::to_vec(&node.clone()).unwrap(); + send.send_response(Response::builder().body(Body::from(response)).unwrap()); + Ok(self) + } + + async fn handler_node_cordon(mut self,osinstance: OSInstance) -> Result{ + let (request, send) = self.0.next_request().await.expect("service not called"); + assert_eq!(request.method(), http::Method::PATCH); + assert_eq!( + request.uri().to_string(), + format!( + "/api/v1/nodes/{}?", + osinstance.name() + ) + ); + assert_eq!(request.extensions().get(),Some(&"cordon")); + let node = Node{ + metadata:ObjectMeta { name:Some(String::from("openeuler")), + ..Default::default()}, + spec:Some(NodeSpec{ + unschedulable:Some(true), + ..Default::default() + }), + status:Some(NodeStatus{ + node_info:Some(NodeSystemInfo{ + os_image: String::from("KubeOS v1"), + ..Default::default() + }), + ..Default::default() + }) + }; + dbg!("handler_node_cordon"); + let response = serde_json::to_vec(&node.clone()).unwrap(); + send.send_response(Response::builder().body(Body::from(response)).unwrap()); + Ok(self) + } + + async fn handler_node_uncordon(mut self,osinstance: OSInstance) -> Result{ + let (request, send) = self.0.next_request().await.expect("service not called"); + assert_eq!(request.method(), http::Method::PATCH); + assert_eq!( + request.uri().to_string(), + format!( + "/api/v1/nodes/{}?", + osinstance.name() + ) + ); + assert_eq!(request.extensions().get(),Some(&"cordon")); + let node = Node{ + metadata:ObjectMeta { name:Some(String::from("openeuler")), + ..Default::default()}, + spec:Some(NodeSpec{ + unschedulable:Some(false), + ..Default::default() + }), + status:Some(NodeStatus{ + node_info:Some(NodeSystemInfo{ + os_image: String::from("KubeOS v1"), + ..Default::default() + }), + ..Default::default() + }) + }; + dbg!("handler_node_uncordon"); + let response = serde_json::to_vec(&node.clone()).unwrap(); + send.send_response(Response::builder().body(Body::from(response)).unwrap()); + Ok(self) + } + + async fn handler_node_pod_list_get(mut self,osinstance: OSInstance) -> Result{ + let (request, send) = self.0.next_request().await.expect("service not called"); + assert_eq!(request.method(), http::Method::GET); + assert_eq!( + request.uri().to_string(), + format!( + "/api/v1/pods?&fieldSelector=spec.nodeName%3D{}", + osinstance.name() + ) + ); + assert_eq!(request.extensions().get(),Some(&"list")); + let pods_list = ObjectList::{ + metadata:ListMeta::default(), + items:vec![] + }; + dbg!("handler_node_pod_list_get"); + let response = serde_json::to_vec(&pods_list).unwrap(); + send.send_response(Response::builder().body(Body::from(response)).unwrap()); + Ok(self) + } +} + +pub mod mock_error { + use thiserror::Error; + #[derive(Error, Debug)] + pub enum Error { + #[error("Kubernetes reported error: {source}")] + KubeError { + #[from] + source: kube::Error, + }, + } +} + +impl ProxyController { + pub fn test() -> (ProxyController, ApiServerVerifier) { + let (mock_service, handle) = tower_test::mock::pair::, Response>(); + let mock_k8s_client = Client::new(mock_service, "default"); + let mock_api_client = ControllerClient::new(mock_k8s_client.clone()); + let mut mock_agent_client: MockAgentMethod = MockAgentMethod::new(); + mock_agent_client.expect_rollback_method().returning(|_x| Ok(())); + mock_agent_client.expect_prepare_upgrade_method().returning(|_x,_y| Ok(())); + mock_agent_client.expect_upgrade_method().returning(|_x| Ok(())); + mock_agent_client.expect_configure_method().returning(|_x,_y| Ok(())); + let proxy_controller: ProxyController = + ProxyController::new(mock_k8s_client, mock_api_client, mock_agent_client); + (proxy_controller, ApiServerVerifier(handle)) + } +} + +impl OSInstance { + pub fn set_osi_default(node_name: &str, namespace: &str) -> Self { + // return osinstance with nodestatus = idle, upgradeconfig.version=v1, sysconfig.version=v1 + let mut labels = BTreeMap::new(); + labels.insert(LABEL_OSINSTANCE.to_string(), node_name.to_string()); + OSInstance { + metadata: ObjectMeta { + name: Some(node_name.to_string()), + namespace: Some(namespace.to_string()), + labels: Some(labels), + ..ObjectMeta::default() + }, + spec: OSInstanceSpec { + nodestatus: NODE_STATUS_IDLE.to_string(), + sysconfigs: Some(Configs{ + version: Some(String::from("v1")), + configs: None + }), + upgradeconfigs: Some(Configs{ + version: Some(String::from("v1")), + configs: None + }), + }, + status: Some(OSInstanceStatus{ + sysconfigs: Some(Configs{ + version: Some(String::from("v1")), + configs: None + }), + upgradeconfigs: Some(Configs{ + version: Some(String::from("v1")), + configs: None + }), + }), + } + } + + pub fn set_osi_nodestatus_upgrade(node_name: &str, namespace: &str) -> Self { + // return osinstance with nodestatus = upgrade, upgradeconfig.version=v1, sysconfig.version=v1 + let mut osinstance = OSInstance::set_osi_default(node_name, namespace); + osinstance.spec.nodestatus = NODE_STATUS_UPGRADE.to_string(); + osinstance + } + + pub fn set_osi_nodestatus_config(node_name: &str, namespace: &str) -> Self { + // return osinstance with nodestatus = upgrade, upgradeconfig.version=v1, sysconfig.version=v1 + let mut osinstance = OSInstance::set_osi_default(node_name, namespace); + osinstance.spec.nodestatus = NODE_STATUS_CONFIG.to_string(); + osinstance + } + + pub fn set_osi_upgradecon_v2(node_name: &str, namespace: &str) -> Self { + // return osinstance with nodestatus = idle, upgradeconfig.version=v1, sysconfig.version=v1 + let mut osinstance = OSInstance::set_osi_default(node_name, namespace); + osinstance.spec.upgradeconfigs.as_mut().unwrap().version = Some(String::from("v2")); + osinstance + } + + pub fn set_osi_nodestatus_upgrade_upgradecon_v2(node_name: &str, namespace: &str) -> Self { + // return osinstance with nodestatus = upgrade, upgradeconfig.version=v2, sysconfig.version=v1 + let mut osinstance = OSInstance::set_osi_default(node_name, namespace); + osinstance.spec.nodestatus = NODE_STATUS_UPGRADE.to_string(); + osinstance.spec.upgradeconfigs.as_mut().unwrap().version = Some(String::from("v2")); + osinstance + } + + pub fn set_osi_nodestatus_config_syscon_v2(node_name: &str, namespace: &str) -> Self { + // return osinstance with nodestatus = upgrade, upgradeconfig.version=v2, sysconfig.version=v1 + let mut osinstance = OSInstance::set_osi_default(node_name, namespace); + osinstance.spec.nodestatus = NODE_STATUS_CONFIG.to_string(); + osinstance.spec.sysconfigs.as_mut().unwrap().version = Some(String::from("v2")); + osinstance + } +} + + +impl OS { + pub fn set_os_default() -> Self { + let mut os = OS::new("test", OSSpec::default()); + os.meta_mut().namespace = Some("default".into()); + os + } + + pub fn set_os_osversion_v2_opstype_config() -> Self { + let mut os = OS::set_os_default(); + os.spec.osversion = String::from("KubeOS v2"); + os.spec.opstype = String::from("config"); + os + } + + pub fn set_os_osversion_v2_upgradecon_v2() -> Self { + let mut os = OS::set_os_default(); + os.spec.osversion = String::from("KubeOS v2"); + os.spec.upgradeconfigs = Some(Configs{ + version: Some(String::from("v2")), + configs: None + }); + os + } + + pub fn set_os_syscon_v2_opstype_config()-> Self { + let mut os = OS::set_os_default(); + os.spec.opstype = String::from("config"); + os.spec.sysconfigs = Some(Configs{ + version: Some(String::from("v2")), + configs: None + }); + os + } + +} + +impl Default for OSSpec{ + fn default() -> Self { + OSSpec { + osversion: String::from("KubeOS v1"), + maxunavailable: 2, + checksum: String::from("test"), + imagetype: String::from("containerd"), + containerimage: String::from("test"), + opstype: String::from("upgrade"), + evictpodforce: true, + sysconfigs: Some(Configs{ + version: Some(String::from("v1")), + configs: None + }), + upgradeconfigs: Some(Configs{ + version: Some(String::from("v1")), + configs: None + }) + } + } +} + diff --git a/KubeOS-Rust/proxy/src/controller/controller.rs b/KubeOS-Rust/proxy/src/controller/controller.rs index b23ec187..264d506d 100644 --- a/KubeOS-Rust/proxy/src/controller/controller.rs +++ b/KubeOS-Rust/proxy/src/controller/controller.rs @@ -24,7 +24,7 @@ use log::{debug, error, info}; use reconciler_error::Error; use super::{ - agentclient::{agent_call::AgentCallClient, AgentMethod, ConfigInfo, KeyInfo, Sysconfig, UpgradeInfo}, + agentclient::{AgentMethod, ConfigInfo, KeyInfo, Sysconfig, UpgradeInfo}, apiclient::ApplyApi, crd::{Configs, Content, OSInstance, OS}, drain::drain_os, @@ -34,6 +34,10 @@ use super::{ REQUEUE_ERROR, REQUEUE_NORMAL, }, }; +#[cfg(test)] +use mockall_double::double; +#[cfg_attr(test, double)] +use super::agentclient::agent_call::AgentCallClient; pub async fn reconcile( os: OS, @@ -423,3 +427,102 @@ pub mod reconciler_error { DrainNodeError { value: String }, } } + +#[cfg(test)] +mod test { + use super::{reconcile,error_policy,ProxyController,OSInstance,OS,Context}; + use crate::controller::ControllerClient; + use crate::controller::{apiserver_mock::{timeout_after_5s,Testcases}, agentclient::MockAgentMethod}; + use std::env; + + #[tokio::test] + async fn test_create_osinstance_with_no_upgrade_or_configuration(){ + let (test_proxy_controller,fakeserver) = ProxyController::::test(); + env::set_var("NODE_NAME", "openeuler"); + let os = OS::set_os_default(); + let context = Context::new(test_proxy_controller); + let mocksrv = fakeserver.run(Testcases::OSInstanceNotExist(OSInstance::set_osi_default("openeuler", "default"))); + reconcile(os,context.clone() ).await.expect("reconciler"); + timeout_after_5s(mocksrv).await; + } + #[tokio::test] + async fn test_upgrade_normal(){ + let (test_proxy_controller,fakeserver) = ProxyController::::test(); + env::set_var("NODE_NAME", "openeuler"); + let os = OS::set_os_osversion_v2_upgradecon_v2(); + let context = Context::new(test_proxy_controller); + let mocksrv = fakeserver.run(Testcases::UpgradeNormal(OSInstance::set_osi_nodestatus_upgrade_upgradecon_v2("openeuler", "default"))); + reconcile(os,context.clone() ).await.expect("reconciler"); + timeout_after_5s(mocksrv).await; + } + + #[tokio::test] + async fn test_diff_osversion_opstype_config(){ + let (test_proxy_controller,fakeserver) = ProxyController::::test(); + env::set_var("NODE_NAME", "openeuler"); + let os = OS::set_os_osversion_v2_opstype_config(); + let context = Context::new(test_proxy_controller); + let mocksrv = fakeserver.run(Testcases::UpgradeOSInstaceNodestatusConfig(OSInstance::set_osi_nodestatus_upgrade_upgradecon_v2("openeuler", "default"))); + let res = reconcile(os,context.clone()).await; + timeout_after_5s(mocksrv).await; + assert!(res.is_err(),"upgrade fails due to opstype=config"); + let err = res.unwrap_err(); + assert!(err.to_string().contains("Expect OS Version is not same with Node OS Version, please upgrade first")); + error_policy(&err, context); + } + + #[tokio::test] + async fn test_upgradeconfigs_version_mismatch(){ + let (test_proxy_controller,fakeserver) = ProxyController::::test(); + env::set_var("NODE_NAME", "openeuler"); + let os = OS::set_os_osversion_v2_upgradecon_v2(); + let context = Context::new(test_proxy_controller); + let mocksrv = fakeserver.run(Testcases::UpgradeUpgradeconfigsVersionMismatch(OSInstance::set_osi_nodestatus_upgrade("openeuler", "default"))); + reconcile(os,context.clone()).await.expect("reconciler"); + timeout_after_5s(mocksrv).await; + } + + #[tokio::test] + async fn test_upgrade_nodestatus_idle(){ + let (test_proxy_controller,fakeserver) = ProxyController::::test(); + env::set_var("NODE_NAME", "openeuler"); + let os = OS::set_os_osversion_v2_upgradecon_v2(); + let context = Context::new(test_proxy_controller); + let mocksrv = fakeserver.run(Testcases::UpgradeOSInstaceNodestatusIdle(OSInstance::set_osi_upgradecon_v2("openeuler", "default"))); + reconcile(os,context.clone()).await.expect("reconciler"); + timeout_after_5s(mocksrv).await; + } + + #[tokio::test] + async fn test_config_normal(){ + let (test_proxy_controller,fakeserver) = ProxyController::::test(); + env::set_var("NODE_NAME", "openeuler"); + let os = OS::set_os_syscon_v2_opstype_config(); + let context = Context::new(test_proxy_controller); + let mocksrv = fakeserver.run(Testcases::ConfigNormal(OSInstance::set_osi_nodestatus_config_syscon_v2("openeuler", "default"))); + reconcile(os,context.clone()).await.expect("reconciler"); + timeout_after_5s(mocksrv).await; + } + + #[tokio::test] + async fn test_sysconfig_version_mismatch_reassign(){ + let (test_proxy_controller,fakeserver) = ProxyController::::test(); + env::set_var("NODE_NAME", "openeuler"); + let os = OS::set_os_syscon_v2_opstype_config(); + let context = Context::new(test_proxy_controller); + let mocksrv = fakeserver.run(Testcases::ConfigVersionMismatchReassign(OSInstance::set_osi_nodestatus_config("openeuler", "default"))); + reconcile(os,context.clone()).await.expect("reconciler"); + timeout_after_5s(mocksrv).await; + } + + #[tokio::test] + async fn test_sysconfig_version_mismatch_update(){ + let (test_proxy_controller,fakeserver) = ProxyController::::test(); + env::set_var("NODE_NAME", "openeuler"); + let os = OS::set_os_syscon_v2_opstype_config(); + let context = Context::new(test_proxy_controller); + let mocksrv = fakeserver.run(Testcases::ConfigVersionMismatchUpdate(OSInstance::set_osi_nodestatus_upgrade("openeuler", "default"))); + reconcile(os,context.clone()).await.expect("reconciler"); + timeout_after_5s(mocksrv).await; + } +} diff --git a/KubeOS-Rust/proxy/src/controller/mod.rs b/KubeOS-Rust/proxy/src/controller/mod.rs index 090febc6..b143082e 100644 --- a/KubeOS-Rust/proxy/src/controller/mod.rs +++ b/KubeOS-Rust/proxy/src/controller/mod.rs @@ -13,6 +13,8 @@ mod agentclient; mod apiclient; mod controller; +#[cfg(test)] +mod apiserver_mock; mod crd; mod drain; mod utils; -- Gitee From d7802750a31a7d7bfa8ff9cede44b4466daa8c26 Mon Sep 17 00:00:00 2001 From: liyuanr Date: Tue, 9 Jan 2024 20:15:08 +0800 Subject: [PATCH 2/2] proxy: support upgrade using disk image or docker and format Added the imageurl, flagSafe, mtls, cacert, clientcert, and clientkey parameters required for disk image upgrade or Docker upgrade. And format code. Change the value of mockall_double to 0.2.1 to adapt to rust 1.57. Signed-off-by: liyuanr --- KubeOS-Rust/Cargo.lock | 6 +- KubeOS-Rust/proxy/Cargo.toml | 2 +- .../proxy/src/controller/agentclient.rs | 27 +- .../proxy/src/controller/apiserver_mock.rs | 488 ++++++++---------- .../proxy/src/controller/controller.rs | 115 +++-- KubeOS-Rust/proxy/src/controller/crd.rs | 7 + KubeOS-Rust/proxy/src/controller/mod.rs | 2 +- 7 files changed, 329 insertions(+), 318 deletions(-) diff --git a/KubeOS-Rust/Cargo.lock b/KubeOS-Rust/Cargo.lock index 19bdc0c7..4b7fc12d 100644 --- a/KubeOS-Rust/Cargo.lock +++ b/KubeOS-Rust/Cargo.lock @@ -1192,14 +1192,14 @@ dependencies = [ [[package]] name = "mockall_double" -version = "0.3.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1ca96e5ac35256ae3e13536edd39b172b88f41615e1d7b653c8ad24524113e8" +checksum = "7dffc15b97456ecc84d2bde8c1df79145e154f45225828c4361f676e1b82acd6" dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.37", + "syn 1.0.109", ] [[package]] diff --git a/KubeOS-Rust/proxy/Cargo.toml b/KubeOS-Rust/proxy/Cargo.toml index ab98d283..9a148e89 100644 --- a/KubeOS-Rust/proxy/Cargo.toml +++ b/KubeOS-Rust/proxy/Cargo.toml @@ -38,4 +38,4 @@ http = "0.2.9" hyper = "0.14.25" tower-test = "0.4.0" mockall = { version = "=0.11.3" } -mockall_double = "0.3.0" +mockall_double = "0.2.1" diff --git a/KubeOS-Rust/proxy/src/controller/agentclient.rs b/KubeOS-Rust/proxy/src/controller/agentclient.rs index cc3ab079..73489a9b 100644 --- a/KubeOS-Rust/proxy/src/controller/agentclient.rs +++ b/KubeOS-Rust/proxy/src/controller/agentclient.rs @@ -22,18 +22,24 @@ use cli::{ }; use manager::api::{CertsInfo, ConfigureRequest, KeyInfo as AgentKeyInfo, Sysconfig as AgentSysconfig, UpgradeRequest}; +#[cfg_attr(test, double)] +use agent_call::AgentCallClient; #[cfg(test)] use mockall::automock; #[cfg(test)] use mockall_double::double; -#[cfg_attr(test, double)] -use agent_call::AgentCallClient; pub struct UpgradeInfo { pub version: String, pub image_type: String, pub check_sum: String, pub container_image: String, + pub imageurl: String, + pub flagsafe: bool, + pub mtls: bool, + pub cacert: String, + pub clientcert: String, + pub clientkey: String, } pub struct ConfigInfo { @@ -63,10 +69,10 @@ pub mod agent_call { use super::{Client, Error, RpcMethod}; #[cfg(test)] use mockall::automock; - + #[derive(Default)] pub struct AgentCallClient {} - + #[cfg_attr(test, automock)] impl AgentCallClient { pub fn call_agent(&self, client: &Client, method: T) -> Result<(), Error> { @@ -95,11 +101,14 @@ impl AgentMethod for AgentClient { image_type: upgrade_info.image_type, check_sum: upgrade_info.check_sum, container_image: upgrade_info.container_image, - // TODO: add image_url, flag_safe, mtls, certs - image_url: "".to_string(), - flag_safe: false, - mtls: false, - certs: CertsInfo { ca_cert: "".to_string(), client_cert: "".to_string(), client_key: "".to_string() }, + image_url: upgrade_info.imageurl, + flag_safe: upgrade_info.flagsafe, + mtls: upgrade_info.mtls, + certs: CertsInfo { + ca_cert: upgrade_info.cacert, + client_cert: upgrade_info.clientcert, + client_key: upgrade_info.clientkey, + }, }; match agent_call.call_agent(&self.agent_client, PrepareUpgradeMethod::new(upgrade_request)) { Ok(_resp) => Ok(()), diff --git a/KubeOS-Rust/proxy/src/controller/apiserver_mock.rs b/KubeOS-Rust/proxy/src/controller/apiserver_mock.rs index a7ae647a..c46d26a4 100644 --- a/KubeOS-Rust/proxy/src/controller/apiserver_mock.rs +++ b/KubeOS-Rust/proxy/src/controller/apiserver_mock.rs @@ -1,21 +1,26 @@ use self::mock_error::Error; +use super::{ + agentclient::*, + crd::{Configs, OSInstanceStatus}, + values::{NODE_STATUS_CONFIG, NODE_STATUS_UPGRADE}, +}; use crate::controller::{ apiclient::{ApplyApi, ControllerClient}, crd::{OSInstance, OSInstanceSpec, OSSpec, OS}, - values::{LABEL_OSINSTANCE, NODE_STATUS_IDLE,LABEL_UPGRADING}, + values::{LABEL_OSINSTANCE, LABEL_UPGRADING, NODE_STATUS_IDLE}, ProxyController, - }; -use super::{agentclient::*, values::{NODE_STATUS_UPGRADE, NODE_STATUS_CONFIG}, crd::{Configs, OSInstanceStatus}}; use anyhow::Result; use http::{Request, Response}; use hyper::{body::to_bytes, Body}; -use kube::{api::ObjectMeta, core::{ObjectList, ListMeta}}; +use k8s_openapi::api::core::v1::Pod; +use k8s_openapi::api::core::v1::{Node, NodeSpec, NodeStatus, NodeSystemInfo}; +use kube::{ + api::ObjectMeta, + core::{ListMeta, ObjectList}, +}; use kube::{Client, Resource, ResourceExt}; use std::collections::BTreeMap; -use k8s_openapi::api::core::v1::{Node, NodeStatus, NodeSystemInfo, NodeSpec}; -use k8s_openapi::api::core::v1::Pod; - type ApiServerHandle = tower_test::mock::Handle, Response>; pub struct ApiServerVerifier(ApiServerHandle); @@ -43,95 +48,142 @@ impl ApiServerVerifier { tokio::spawn(async move { match cases { Testcases::OSInstanceNotExist(osi) => { - self.handler_osinstance_get_not_exist(osi.clone()).await.unwrap() - .handler_osinstance_creation(osi.clone()).await.unwrap() - .handler_osinstance_get_exist(osi.clone()).await.unwrap() - .handler_node_get(osi).await + self.handler_osinstance_get_not_exist(osi.clone()) + .await + .unwrap() + .handler_osinstance_creation(osi.clone()) + .await + .unwrap() + .handler_osinstance_get_exist(osi.clone()) + .await + .unwrap() + .handler_node_get(osi) + .await }, Testcases::UpgradeNormal(osi) => { self.handler_osinstance_get_exist(osi.clone()) - .await.unwrap().handler_osinstance_get_exist(osi.clone()) - .await.unwrap().handler_node_get_with_label(osi.clone()) - .await.unwrap().handler_osinstance_patch_upgradeconfig_v2(osi.clone()) - .await.unwrap().handler_node_cordon(osi.clone()) - .await.unwrap().handler_node_pod_list_get(osi).await + .await + .unwrap() + .handler_osinstance_get_exist(osi.clone()) + .await + .unwrap() + .handler_node_get_with_label(osi.clone()) + .await + .unwrap() + .handler_osinstance_patch_upgradeconfig_v2(osi.clone()) + .await + .unwrap() + .handler_node_cordon(osi.clone()) + .await + .unwrap() + .handler_node_pod_list_get(osi) + .await }, Testcases::UpgradeUpgradeconfigsVersionMismatch(osi) => { self.handler_osinstance_get_exist(osi.clone()) - .await.unwrap().handler_osinstance_get_exist(osi.clone()) - .await.unwrap().handler_node_get_with_label(osi.clone()) - .await.unwrap().handler_node_update_delete_label(osi.clone()) - .await.unwrap().handler_node_uncordon(osi.clone()) - .await.unwrap().handler_osinstance_patch_nodestatus_idle(osi) - .await + .await + .unwrap() + .handler_osinstance_get_exist(osi.clone()) + .await + .unwrap() + .handler_node_get_with_label(osi.clone()) + .await + .unwrap() + .handler_node_update_delete_label(osi.clone()) + .await + .unwrap() + .handler_node_uncordon(osi.clone()) + .await + .unwrap() + .handler_osinstance_patch_nodestatus_idle(osi) + .await }, Testcases::UpgradeOSInstaceNodestatusConfig(osi) => { self.handler_osinstance_get_exist(osi.clone()) - .await.unwrap().handler_osinstance_get_exist(osi.clone()) - .await.unwrap().handler_node_get_with_label(osi.clone()) - .await + .await + .unwrap() + .handler_osinstance_get_exist(osi.clone()) + .await + .unwrap() + .handler_node_get_with_label(osi.clone()) + .await }, Testcases::UpgradeOSInstaceNodestatusIdle(osi) => { self.handler_osinstance_get_exist(osi.clone()) - .await.unwrap().handler_osinstance_get_exist(osi.clone()) - .await.unwrap().handler_node_get_with_label(osi.clone()) - .await.unwrap().handler_node_update_delete_label(osi.clone()) - .await.unwrap().handler_node_uncordon(osi) - .await + .await + .unwrap() + .handler_osinstance_get_exist(osi.clone()) + .await + .unwrap() + .handler_node_get_with_label(osi.clone()) + .await + .unwrap() + .handler_node_update_delete_label(osi.clone()) + .await + .unwrap() + .handler_node_uncordon(osi) + .await }, Testcases::ConfigNormal(osi) => { self.handler_osinstance_get_exist(osi.clone()) - .await.unwrap().handler_osinstance_get_exist(osi.clone()) - .await.unwrap().handler_node_get(osi.clone()) - .await.unwrap().handler_osinstance_patch_sysconfig_v2(osi.clone()) - .await.unwrap().handler_osinstance_patch_nodestatus_idle(osi) - .await + .await + .unwrap() + .handler_osinstance_get_exist(osi.clone()) + .await + .unwrap() + .handler_node_get(osi.clone()) + .await + .unwrap() + .handler_osinstance_patch_sysconfig_v2(osi.clone()) + .await + .unwrap() + .handler_osinstance_patch_nodestatus_idle(osi) + .await }, Testcases::ConfigVersionMismatchReassign(osi) => { self.handler_osinstance_get_exist(osi.clone()) - .await.unwrap().handler_osinstance_get_exist(osi.clone()) - .await.unwrap().handler_node_get(osi.clone()) - .await.unwrap().handler_osinstance_patch_nodestatus_idle(osi) - .await + .await + .unwrap() + .handler_osinstance_get_exist(osi.clone()) + .await + .unwrap() + .handler_node_get(osi.clone()) + .await + .unwrap() + .handler_osinstance_patch_nodestatus_idle(osi) + .await }, Testcases::ConfigVersionMismatchUpdate(osi) => { self.handler_osinstance_get_exist(osi.clone()) - .await.unwrap().handler_osinstance_get_exist(osi.clone()) - .await.unwrap().handler_node_get(osi.clone()) - .await.unwrap().handler_osinstance_patch_spec_sysconfig_v2(osi) - .await + .await + .unwrap() + .handler_osinstance_get_exist(osi.clone()) + .await + .unwrap() + .handler_node_get(osi.clone()) + .await + .unwrap() + .handler_osinstance_patch_spec_sysconfig_v2(osi) + .await }, - - - - }.expect("Case completed without errors"); + } + .expect("Case completed without errors"); }) } - async fn handler_osinstance_get_not_exist( - mut self, - osinstance: OSInstance, - ) -> Result { + async fn handler_osinstance_get_not_exist(mut self, osinstance: OSInstance) -> Result { let (request, send) = self.0.next_request().await.expect("service not called"); assert_eq!(request.method(), http::Method::GET); assert_eq!( request.uri().to_string(), - format!( - "/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}", - osinstance.name() - ) + format!("/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}", osinstance.name()) ); let response_json = serde_json::json!( { "status": "Failure", "message": "osinstances.upgrade.openeuler.org \"openeuler\" not found", "reason": "NotFound", "code": 404 } ); dbg!("handler_osinstance_get_not_exist"); let response = serde_json::to_vec(&response_json).unwrap(); - send.send_response( - Response::builder() - .status(404) - .body(Body::from(response)) - .unwrap(), - ); + send.send_response(Response::builder().status(404).body(Body::from(response)).unwrap()); Ok(self) } async fn handler_osinstance_get_exist(mut self, osinstance: OSInstance) -> Result { @@ -139,10 +191,7 @@ impl ApiServerVerifier { assert_eq!(request.method(), http::Method::GET); assert_eq!( request.uri().to_string(), - format!( - "/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}", - osinstance.name() - ) + format!("/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}", osinstance.name()) ); dbg!("handler_osinstance_get_exist"); let response = serde_json::to_vec(&osinstance).unwrap(); @@ -167,16 +216,15 @@ impl ApiServerVerifier { assert_eq!(request.method(), http::Method::PATCH); assert_eq!( request.uri().to_string(), - format!("/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}?",osinstance.name()) + format!("/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}?", osinstance.name()) ); let req_body = to_bytes(request.into_body()).await.unwrap(); - let body_json:serde_json::Value= - serde_json::from_slice(&req_body).expect("valid document from runtime"); + let body_json: serde_json::Value = serde_json::from_slice(&req_body).expect("valid document from runtime"); let spec_json = body_json.get("spec").expect("spec object").clone(); - let spec:OSInstanceSpec = serde_json::from_value(spec_json).expect("valid spec"); + let spec: OSInstanceSpec = serde_json::from_value(spec_json).expect("valid spec"); assert_eq!(spec.nodestatus.clone(), NODE_STATUS_IDLE.to_string()); - + dbg!("handler_osinstance_patch_nodestatus_idle"); osinstance.spec.nodestatus = NODE_STATUS_IDLE.to_string(); let response = serde_json::to_vec(&osinstance).unwrap(); @@ -189,17 +237,21 @@ impl ApiServerVerifier { assert_eq!(request.method(), http::Method::PATCH); assert_eq!( request.uri().to_string(), - format!("/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}/status?",osinstance.name()) + format!( + "/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}/status?", + osinstance.name() + ) ); let req_body = to_bytes(request.into_body()).await.unwrap(); - let body_json:serde_json::Value= - serde_json::from_slice(&req_body).expect("valid document from runtime"); + let body_json: serde_json::Value = serde_json::from_slice(&req_body).expect("valid document from runtime"); let status_json = body_json.get("status").expect("status object").clone(); - let status:OSInstanceStatus = serde_json::from_value(status_json).expect("valid status"); - - assert_eq!(status.upgradeconfigs.expect("upgradeconfigs is not None").clone(), - osinstance.spec.clone().upgradeconfigs.expect("upgradeconfig is not None")); + let status: OSInstanceStatus = serde_json::from_value(status_json).expect("valid status"); + + assert_eq!( + status.upgradeconfigs.expect("upgradeconfigs is not None").clone(), + osinstance.spec.clone().upgradeconfigs.expect("upgradeconfig is not None") + ); osinstance.status.as_mut().unwrap().upgradeconfigs = osinstance.spec.upgradeconfigs.clone(); @@ -214,17 +266,21 @@ impl ApiServerVerifier { assert_eq!(request.method(), http::Method::PATCH); assert_eq!( request.uri().to_string(), - format!("/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}/status?",osinstance.name()) + format!( + "/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}/status?", + osinstance.name() + ) ); let req_body = to_bytes(request.into_body()).await.unwrap(); - let body_json:serde_json::Value= - serde_json::from_slice(&req_body).expect("valid osinstance"); + let body_json: serde_json::Value = serde_json::from_slice(&req_body).expect("valid osinstance"); let status_json = body_json.get("status").expect("status object").clone(); - let status:OSInstanceStatus = serde_json::from_value(status_json).expect("valid status"); - - assert_eq!(status.sysconfigs.expect("sysconfigs is not None").clone(), - osinstance.spec.clone().sysconfigs.expect("sysconfig is not None")); + let status: OSInstanceStatus = serde_json::from_value(status_json).expect("valid status"); + + assert_eq!( + status.sysconfigs.expect("sysconfigs is not None").clone(), + osinstance.spec.clone().sysconfigs.expect("sysconfig is not None") + ); osinstance.status.as_mut().unwrap().sysconfigs = osinstance.spec.sysconfigs.clone(); @@ -239,17 +295,18 @@ impl ApiServerVerifier { assert_eq!(request.method(), http::Method::PATCH); assert_eq!( request.uri().to_string(), - format!("/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}?",osinstance.name()) + format!("/apis/upgrade.openeuler.org/v1alpha1/namespaces/default/osinstances/{}?", osinstance.name()) ); let req_body = to_bytes(request.into_body()).await.unwrap(); - let body_json:serde_json::Value= - serde_json::from_slice(&req_body).expect("valid osinstance"); + let body_json: serde_json::Value = serde_json::from_slice(&req_body).expect("valid osinstance"); let spec_json = body_json.get("spec").expect("spec object").clone(); - let spec:OSInstanceSpec = serde_json::from_value(spec_json).expect("valid spec"); - - assert_eq!(spec.sysconfigs.expect("upgradeconfigs is not None").clone().version.clone().unwrap(), - String::from("v2")); + let spec: OSInstanceSpec = serde_json::from_value(spec_json).expect("valid spec"); + + assert_eq!( + spec.sysconfigs.expect("upgradeconfigs is not None").clone().version.clone().unwrap(), + String::from("v2") + ); osinstance.spec.sysconfigs.as_mut().unwrap().version = Some(String::from("v2")); @@ -259,65 +316,44 @@ impl ApiServerVerifier { Ok(self) } - - async fn handler_node_get(mut self,osinstance: OSInstance) -> Result{ + async fn handler_node_get(mut self, osinstance: OSInstance) -> Result { // return node with name = openeuler, osimage = KubeOS v1,no upgrade label let (request, send) = self.0.next_request().await.expect("service not called"); assert_eq!(request.method(), http::Method::GET); - assert_eq!( - request.uri().to_string(), - format!( - "/api/v1/nodes/{}", - osinstance.name() - ) - ); - let node = Node{ - metadata:ObjectMeta { name:Some(String::from("openeuler")), - ..Default::default()}, - spec:None, - status:Some(NodeStatus{ - node_info:Some(NodeSystemInfo{ - os_image: String::from("KubeOS v1"), - ..Default::default() - }), + assert_eq!(request.uri().to_string(), format!("/api/v1/nodes/{}", osinstance.name())); + let node = Node { + metadata: ObjectMeta { name: Some(String::from("openeuler")), ..Default::default() }, + spec: None, + status: Some(NodeStatus { + node_info: Some(NodeSystemInfo { os_image: String::from("KubeOS v1"), ..Default::default() }), ..Default::default() - }) + }), }; - assert_eq!(node.name(),String::from("openeuler")); - assert_eq!(node.status.as_ref().unwrap().node_info.as_ref().unwrap().os_image,String::from("KubeOS v1")); + assert_eq!(node.name(), String::from("openeuler")); + assert_eq!(node.status.as_ref().unwrap().node_info.as_ref().unwrap().os_image, String::from("KubeOS v1")); dbg!("handler_node_get"); let response = serde_json::to_vec(&node.clone()).unwrap(); send.send_response(Response::builder().body(Body::from(response)).unwrap()); Ok(self) } - async fn handler_node_get_with_label(mut self,osinstance: OSInstance) -> Result{ + async fn handler_node_get_with_label(mut self, osinstance: OSInstance) -> Result { // return node with name = openeuler, osimage = KubeOS v1,has upgrade label let (request, send) = self.0.next_request().await.expect("service not called"); assert_eq!(request.method(), http::Method::GET); - assert_eq!( - request.uri().to_string(), - format!( - "/api/v1/nodes/{}", - osinstance.name() - ) - ); - let mut node = Node{ - metadata:ObjectMeta { name:Some(String::from("openeuler")), - ..Default::default()}, - spec:None, - status:Some(NodeStatus{ - node_info:Some(NodeSystemInfo{ - os_image: String::from("KubeOS v1"), - ..Default::default() - }), + assert_eq!(request.uri().to_string(), format!("/api/v1/nodes/{}", osinstance.name())); + let mut node = Node { + metadata: ObjectMeta { name: Some(String::from("openeuler")), ..Default::default() }, + spec: None, + status: Some(NodeStatus { + node_info: Some(NodeSystemInfo { os_image: String::from("KubeOS v1"), ..Default::default() }), ..Default::default() - }) + }), }; let node_labels = node.labels_mut(); node_labels.insert(LABEL_UPGRADING.to_string(), "".to_string()); - assert_eq!(node.name(),String::from("openeuler")); - assert_eq!(node.status.as_ref().unwrap().node_info.as_ref().unwrap().os_image,String::from("KubeOS v1")); + assert_eq!(node.name(), String::from("openeuler")); + assert_eq!(node.status.as_ref().unwrap().node_info.as_ref().unwrap().os_image, String::from("KubeOS v1")); assert!(node.labels().contains_key(LABEL_UPGRADING)); dbg!("handler_node_get_with_label"); let response = serde_json::to_vec(&node.clone()).unwrap(); @@ -325,32 +361,19 @@ impl ApiServerVerifier { Ok(self) } - async fn handler_node_update_delete_label(mut self,osinstance: OSInstance) -> Result{ + async fn handler_node_update_delete_label(mut self, osinstance: OSInstance) -> Result { // return node with name = openeuler, osimage = KubeOS v1,no upgrade label let (request, send) = self.0.next_request().await.expect("service not called"); assert_eq!(request.method(), http::Method::PUT); - assert_eq!( - request.uri().to_string(), - format!( - "/api/v1/nodes/{}?", - osinstance.name() - ) - ); + assert_eq!(request.uri().to_string(), format!("/api/v1/nodes/{}?", osinstance.name())); // check request body has upgrade label - let node = Node{ - metadata:ObjectMeta { name:Some(String::from("openeuler")), - ..Default::default()}, - spec:Some(NodeSpec{ - unschedulable:Some(true), + let node = Node { + metadata: ObjectMeta { name: Some(String::from("openeuler")), ..Default::default() }, + spec: Some(NodeSpec { unschedulable: Some(true), ..Default::default() }), + status: Some(NodeStatus { + node_info: Some(NodeSystemInfo { os_image: String::from("KubeOS v1"), ..Default::default() }), ..Default::default() }), - status:Some(NodeStatus{ - node_info:Some(NodeSystemInfo{ - os_image: String::from("KubeOS v1"), - ..Default::default() - }), - ..Default::default() - }) }; dbg!("handler_node_update_delete_label"); let response = serde_json::to_vec(&node.clone()).unwrap(); @@ -358,31 +381,18 @@ impl ApiServerVerifier { Ok(self) } - async fn handler_node_cordon(mut self,osinstance: OSInstance) -> Result{ + async fn handler_node_cordon(mut self, osinstance: OSInstance) -> Result { let (request, send) = self.0.next_request().await.expect("service not called"); assert_eq!(request.method(), http::Method::PATCH); - assert_eq!( - request.uri().to_string(), - format!( - "/api/v1/nodes/{}?", - osinstance.name() - ) - ); - assert_eq!(request.extensions().get(),Some(&"cordon")); - let node = Node{ - metadata:ObjectMeta { name:Some(String::from("openeuler")), - ..Default::default()}, - spec:Some(NodeSpec{ - unschedulable:Some(true), + assert_eq!(request.uri().to_string(), format!("/api/v1/nodes/{}?", osinstance.name())); + assert_eq!(request.extensions().get(), Some(&"cordon")); + let node = Node { + metadata: ObjectMeta { name: Some(String::from("openeuler")), ..Default::default() }, + spec: Some(NodeSpec { unschedulable: Some(true), ..Default::default() }), + status: Some(NodeStatus { + node_info: Some(NodeSystemInfo { os_image: String::from("KubeOS v1"), ..Default::default() }), ..Default::default() }), - status:Some(NodeStatus{ - node_info:Some(NodeSystemInfo{ - os_image: String::from("KubeOS v1"), - ..Default::default() - }), - ..Default::default() - }) }; dbg!("handler_node_cordon"); let response = serde_json::to_vec(&node.clone()).unwrap(); @@ -390,31 +400,18 @@ impl ApiServerVerifier { Ok(self) } - async fn handler_node_uncordon(mut self,osinstance: OSInstance) -> Result{ + async fn handler_node_uncordon(mut self, osinstance: OSInstance) -> Result { let (request, send) = self.0.next_request().await.expect("service not called"); assert_eq!(request.method(), http::Method::PATCH); - assert_eq!( - request.uri().to_string(), - format!( - "/api/v1/nodes/{}?", - osinstance.name() - ) - ); - assert_eq!(request.extensions().get(),Some(&"cordon")); - let node = Node{ - metadata:ObjectMeta { name:Some(String::from("openeuler")), - ..Default::default()}, - spec:Some(NodeSpec{ - unschedulable:Some(false), + assert_eq!(request.uri().to_string(), format!("/api/v1/nodes/{}?", osinstance.name())); + assert_eq!(request.extensions().get(), Some(&"cordon")); + let node = Node { + metadata: ObjectMeta { name: Some(String::from("openeuler")), ..Default::default() }, + spec: Some(NodeSpec { unschedulable: Some(false), ..Default::default() }), + status: Some(NodeStatus { + node_info: Some(NodeSystemInfo { os_image: String::from("KubeOS v1"), ..Default::default() }), ..Default::default() }), - status:Some(NodeStatus{ - node_info:Some(NodeSystemInfo{ - os_image: String::from("KubeOS v1"), - ..Default::default() - }), - ..Default::default() - }) }; dbg!("handler_node_uncordon"); let response = serde_json::to_vec(&node.clone()).unwrap(); @@ -422,21 +419,15 @@ impl ApiServerVerifier { Ok(self) } - async fn handler_node_pod_list_get(mut self,osinstance: OSInstance) -> Result{ + async fn handler_node_pod_list_get(mut self, osinstance: OSInstance) -> Result { let (request, send) = self.0.next_request().await.expect("service not called"); assert_eq!(request.method(), http::Method::GET); assert_eq!( request.uri().to_string(), - format!( - "/api/v1/pods?&fieldSelector=spec.nodeName%3D{}", - osinstance.name() - ) + format!("/api/v1/pods?&fieldSelector=spec.nodeName%3D{}", osinstance.name()) ); - assert_eq!(request.extensions().get(),Some(&"list")); - let pods_list = ObjectList::{ - metadata:ListMeta::default(), - items:vec![] - }; + assert_eq!(request.extensions().get(), Some(&"list")); + let pods_list = ObjectList:: { metadata: ListMeta::default(), items: vec![] }; dbg!("handler_node_pod_list_get"); let response = serde_json::to_vec(&pods_list).unwrap(); send.send_response(Response::builder().body(Body::from(response)).unwrap()); @@ -456,16 +447,16 @@ pub mod mock_error { } } -impl ProxyController { - pub fn test() -> (ProxyController, ApiServerVerifier) { +impl ProxyController { + pub fn test() -> (ProxyController, ApiServerVerifier) { let (mock_service, handle) = tower_test::mock::pair::, Response>(); let mock_k8s_client = Client::new(mock_service, "default"); let mock_api_client = ControllerClient::new(mock_k8s_client.clone()); - let mut mock_agent_client: MockAgentMethod = MockAgentMethod::new(); + let mut mock_agent_client: MockAgentMethod = MockAgentMethod::new(); mock_agent_client.expect_rollback_method().returning(|_x| Ok(())); - mock_agent_client.expect_prepare_upgrade_method().returning(|_x,_y| Ok(())); + mock_agent_client.expect_prepare_upgrade_method().returning(|_x, _y| Ok(())); mock_agent_client.expect_upgrade_method().returning(|_x| Ok(())); - mock_agent_client.expect_configure_method().returning(|_x,_y| Ok(())); + mock_agent_client.expect_configure_method().returning(|_x, _y| Ok(())); let proxy_controller: ProxyController = ProxyController::new(mock_k8s_client, mock_api_client, mock_agent_client); (proxy_controller, ApiServerVerifier(handle)) @@ -486,52 +477,40 @@ impl OSInstance { }, spec: OSInstanceSpec { nodestatus: NODE_STATUS_IDLE.to_string(), - sysconfigs: Some(Configs{ - version: Some(String::from("v1")), - configs: None - }), - upgradeconfigs: Some(Configs{ - version: Some(String::from("v1")), - configs: None - }), + sysconfigs: Some(Configs { version: Some(String::from("v1")), configs: None }), + upgradeconfigs: Some(Configs { version: Some(String::from("v1")), configs: None }), }, - status: Some(OSInstanceStatus{ - sysconfigs: Some(Configs{ - version: Some(String::from("v1")), - configs: None - }), - upgradeconfigs: Some(Configs{ - version: Some(String::from("v1")), - configs: None - }), + status: Some(OSInstanceStatus { + sysconfigs: Some(Configs { version: Some(String::from("v1")), configs: None }), + upgradeconfigs: Some(Configs { version: Some(String::from("v1")), configs: None }), }), } } pub fn set_osi_nodestatus_upgrade(node_name: &str, namespace: &str) -> Self { // return osinstance with nodestatus = upgrade, upgradeconfig.version=v1, sysconfig.version=v1 - let mut osinstance = OSInstance::set_osi_default(node_name, namespace); + let mut osinstance = OSInstance::set_osi_default(node_name, namespace); osinstance.spec.nodestatus = NODE_STATUS_UPGRADE.to_string(); osinstance } pub fn set_osi_nodestatus_config(node_name: &str, namespace: &str) -> Self { // return osinstance with nodestatus = upgrade, upgradeconfig.version=v1, sysconfig.version=v1 - let mut osinstance = OSInstance::set_osi_default(node_name, namespace); + let mut osinstance = OSInstance::set_osi_default(node_name, namespace); osinstance.spec.nodestatus = NODE_STATUS_CONFIG.to_string(); osinstance } pub fn set_osi_upgradecon_v2(node_name: &str, namespace: &str) -> Self { // return osinstance with nodestatus = idle, upgradeconfig.version=v1, sysconfig.version=v1 - let mut osinstance = OSInstance::set_osi_default(node_name, namespace); + let mut osinstance = OSInstance::set_osi_default(node_name, namespace); osinstance.spec.upgradeconfigs.as_mut().unwrap().version = Some(String::from("v2")); osinstance } pub fn set_osi_nodestatus_upgrade_upgradecon_v2(node_name: &str, namespace: &str) -> Self { // return osinstance with nodestatus = upgrade, upgradeconfig.version=v2, sysconfig.version=v1 - let mut osinstance = OSInstance::set_osi_default(node_name, namespace); + let mut osinstance = OSInstance::set_osi_default(node_name, namespace); osinstance.spec.nodestatus = NODE_STATUS_UPGRADE.to_string(); osinstance.spec.upgradeconfigs.as_mut().unwrap().version = Some(String::from("v2")); osinstance @@ -539,14 +518,13 @@ impl OSInstance { pub fn set_osi_nodestatus_config_syscon_v2(node_name: &str, namespace: &str) -> Self { // return osinstance with nodestatus = upgrade, upgradeconfig.version=v2, sysconfig.version=v1 - let mut osinstance = OSInstance::set_osi_default(node_name, namespace); + let mut osinstance = OSInstance::set_osi_default(node_name, namespace); osinstance.spec.nodestatus = NODE_STATUS_CONFIG.to_string(); osinstance.spec.sysconfigs.as_mut().unwrap().version = Some(String::from("v2")); osinstance } } - impl OS { pub fn set_os_default() -> Self { let mut os = OS::new("test", OSSpec::default()); @@ -564,44 +542,36 @@ impl OS { pub fn set_os_osversion_v2_upgradecon_v2() -> Self { let mut os = OS::set_os_default(); os.spec.osversion = String::from("KubeOS v2"); - os.spec.upgradeconfigs = Some(Configs{ - version: Some(String::from("v2")), - configs: None - }); + os.spec.upgradeconfigs = Some(Configs { version: Some(String::from("v2")), configs: None }); os } - pub fn set_os_syscon_v2_opstype_config()-> Self { + pub fn set_os_syscon_v2_opstype_config() -> Self { let mut os = OS::set_os_default(); os.spec.opstype = String::from("config"); - os.spec.sysconfigs = Some(Configs{ - version: Some(String::from("v2")), - configs: None - }); + os.spec.sysconfigs = Some(Configs { version: Some(String::from("v2")), configs: None }); os } - } -impl Default for OSSpec{ +impl Default for OSSpec { fn default() -> Self { - OSSpec { - osversion: String::from("KubeOS v1"), - maxunavailable: 2, - checksum: String::from("test"), - imagetype: String::from("containerd"), + OSSpec { + osversion: String::from("KubeOS v1"), + maxunavailable: 2, + checksum: String::from("test"), + imagetype: String::from("containerd"), containerimage: String::from("test"), - opstype: String::from("upgrade"), - evictpodforce: true, - sysconfigs: Some(Configs{ - version: Some(String::from("v1")), - configs: None - }), - upgradeconfigs: Some(Configs{ - version: Some(String::from("v1")), - configs: None - }) + opstype: String::from("upgrade"), + evictpodforce: true, + imageurl: String::from(""), + flagsafe: false, + mtls: false, + cacert: Some(String::from("")), + clientcert: Some(String::from("")), + clientkey: Some(String::from("")), + sysconfigs: Some(Configs { version: Some(String::from("v1")), configs: None }), + upgradeconfigs: Some(Configs { version: Some(String::from("v1")), configs: None }), } } } - diff --git a/KubeOS-Rust/proxy/src/controller/controller.rs b/KubeOS-Rust/proxy/src/controller/controller.rs index 264d506d..e7ee9f9b 100644 --- a/KubeOS-Rust/proxy/src/controller/controller.rs +++ b/KubeOS-Rust/proxy/src/controller/controller.rs @@ -23,6 +23,8 @@ use kube::{ use log::{debug, error, info}; use reconciler_error::Error; +#[cfg_attr(test, double)] +use super::agentclient::agent_call::AgentCallClient; use super::{ agentclient::{AgentMethod, ConfigInfo, KeyInfo, Sysconfig, UpgradeInfo}, apiclient::ApplyApi, @@ -36,8 +38,6 @@ use super::{ }; #[cfg(test)] use mockall_double::double; -#[cfg_attr(test, double)] -use super::agentclient::agent_call::AgentCallClient; pub async fn reconcile( os: OS, @@ -283,6 +283,12 @@ impl ProxyController { image_type: os_cr.spec.imagetype.clone(), check_sum: os_cr.spec.checksum.clone(), container_image: os_cr.spec.containerimage.clone(), + flagsafe: os_cr.spec.flagsafe.clone(), + imageurl: os_cr.spec.imageurl.clone(), + mtls: os_cr.spec.mtls.clone(), + cacert: os_cr.spec.cacert.clone().unwrap_or_default(), + clientcert: os_cr.spec.clientcert.clone().unwrap_or_default(), + clientkey: os_cr.spec.clientkey.clone().unwrap_or_default(), }; let agent_call_client = AgentCallClient::default(); match self.agent_client.prepare_upgrade_method(upgrade_info, agent_call_client) { @@ -430,99 +436,118 @@ pub mod reconciler_error { #[cfg(test)] mod test { - use super::{reconcile,error_policy,ProxyController,OSInstance,OS,Context}; + use super::{error_policy, reconcile, Context, OSInstance, ProxyController, OS}; use crate::controller::ControllerClient; - use crate::controller::{apiserver_mock::{timeout_after_5s,Testcases}, agentclient::MockAgentMethod}; + use crate::controller::{ + agentclient::MockAgentMethod, + apiserver_mock::{timeout_after_5s, Testcases}, + }; use std::env; #[tokio::test] - async fn test_create_osinstance_with_no_upgrade_or_configuration(){ - let (test_proxy_controller,fakeserver) = ProxyController::::test(); + async fn test_create_osinstance_with_no_upgrade_or_configuration() { + let (test_proxy_controller, fakeserver) = ProxyController::::test(); env::set_var("NODE_NAME", "openeuler"); let os = OS::set_os_default(); - let context = Context::new(test_proxy_controller); - let mocksrv = fakeserver.run(Testcases::OSInstanceNotExist(OSInstance::set_osi_default("openeuler", "default"))); - reconcile(os,context.clone() ).await.expect("reconciler"); + let context = Context::new(test_proxy_controller); + let mocksrv = + fakeserver.run(Testcases::OSInstanceNotExist(OSInstance::set_osi_default("openeuler", "default"))); + reconcile(os, context.clone()).await.expect("reconciler"); timeout_after_5s(mocksrv).await; } #[tokio::test] - async fn test_upgrade_normal(){ - let (test_proxy_controller,fakeserver) = ProxyController::::test(); + async fn test_upgrade_normal() { + let (test_proxy_controller, fakeserver) = ProxyController::::test(); env::set_var("NODE_NAME", "openeuler"); let os = OS::set_os_osversion_v2_upgradecon_v2(); - let context = Context::new(test_proxy_controller); - let mocksrv = fakeserver.run(Testcases::UpgradeNormal(OSInstance::set_osi_nodestatus_upgrade_upgradecon_v2("openeuler", "default"))); - reconcile(os,context.clone() ).await.expect("reconciler"); + let context = Context::new(test_proxy_controller); + let mocksrv = fakeserver.run(Testcases::UpgradeNormal(OSInstance::set_osi_nodestatus_upgrade_upgradecon_v2( + "openeuler", + "default", + ))); + reconcile(os, context.clone()).await.expect("reconciler"); timeout_after_5s(mocksrv).await; } #[tokio::test] - async fn test_diff_osversion_opstype_config(){ - let (test_proxy_controller,fakeserver) = ProxyController::::test(); + async fn test_diff_osversion_opstype_config() { + let (test_proxy_controller, fakeserver) = ProxyController::::test(); env::set_var("NODE_NAME", "openeuler"); let os = OS::set_os_osversion_v2_opstype_config(); - let context = Context::new(test_proxy_controller); - let mocksrv = fakeserver.run(Testcases::UpgradeOSInstaceNodestatusConfig(OSInstance::set_osi_nodestatus_upgrade_upgradecon_v2("openeuler", "default"))); - let res = reconcile(os,context.clone()).await; + let context = Context::new(test_proxy_controller); + let mocksrv = fakeserver.run(Testcases::UpgradeOSInstaceNodestatusConfig( + OSInstance::set_osi_nodestatus_upgrade_upgradecon_v2("openeuler", "default"), + )); + let res = reconcile(os, context.clone()).await; timeout_after_5s(mocksrv).await; - assert!(res.is_err(),"upgrade fails due to opstype=config"); + assert!(res.is_err(), "upgrade fails due to opstype=config"); let err = res.unwrap_err(); assert!(err.to_string().contains("Expect OS Version is not same with Node OS Version, please upgrade first")); error_policy(&err, context); } #[tokio::test] - async fn test_upgradeconfigs_version_mismatch(){ - let (test_proxy_controller,fakeserver) = ProxyController::::test(); + async fn test_upgradeconfigs_version_mismatch() { + let (test_proxy_controller, fakeserver) = ProxyController::::test(); env::set_var("NODE_NAME", "openeuler"); let os = OS::set_os_osversion_v2_upgradecon_v2(); - let context = Context::new(test_proxy_controller); - let mocksrv = fakeserver.run(Testcases::UpgradeUpgradeconfigsVersionMismatch(OSInstance::set_osi_nodestatus_upgrade("openeuler", "default"))); - reconcile(os,context.clone()).await.expect("reconciler"); + let context = Context::new(test_proxy_controller); + let mocksrv = fakeserver.run(Testcases::UpgradeUpgradeconfigsVersionMismatch( + OSInstance::set_osi_nodestatus_upgrade("openeuler", "default"), + )); + reconcile(os, context.clone()).await.expect("reconciler"); timeout_after_5s(mocksrv).await; } #[tokio::test] - async fn test_upgrade_nodestatus_idle(){ - let (test_proxy_controller,fakeserver) = ProxyController::::test(); + async fn test_upgrade_nodestatus_idle() { + let (test_proxy_controller, fakeserver) = ProxyController::::test(); env::set_var("NODE_NAME", "openeuler"); let os = OS::set_os_osversion_v2_upgradecon_v2(); - let context = Context::new(test_proxy_controller); - let mocksrv = fakeserver.run(Testcases::UpgradeOSInstaceNodestatusIdle(OSInstance::set_osi_upgradecon_v2("openeuler", "default"))); - reconcile(os,context.clone()).await.expect("reconciler"); + let context = Context::new(test_proxy_controller); + let mocksrv = fakeserver + .run(Testcases::UpgradeOSInstaceNodestatusIdle(OSInstance::set_osi_upgradecon_v2("openeuler", "default"))); + reconcile(os, context.clone()).await.expect("reconciler"); timeout_after_5s(mocksrv).await; } #[tokio::test] - async fn test_config_normal(){ - let (test_proxy_controller,fakeserver) = ProxyController::::test(); + async fn test_config_normal() { + let (test_proxy_controller, fakeserver) = ProxyController::::test(); env::set_var("NODE_NAME", "openeuler"); let os = OS::set_os_syscon_v2_opstype_config(); - let context = Context::new(test_proxy_controller); - let mocksrv = fakeserver.run(Testcases::ConfigNormal(OSInstance::set_osi_nodestatus_config_syscon_v2("openeuler", "default"))); - reconcile(os,context.clone()).await.expect("reconciler"); + let context = Context::new(test_proxy_controller); + let mocksrv = fakeserver + .run(Testcases::ConfigNormal(OSInstance::set_osi_nodestatus_config_syscon_v2("openeuler", "default"))); + reconcile(os, context.clone()).await.expect("reconciler"); timeout_after_5s(mocksrv).await; } #[tokio::test] - async fn test_sysconfig_version_mismatch_reassign(){ - let (test_proxy_controller,fakeserver) = ProxyController::::test(); + async fn test_sysconfig_version_mismatch_reassign() { + let (test_proxy_controller, fakeserver) = ProxyController::::test(); env::set_var("NODE_NAME", "openeuler"); let os = OS::set_os_syscon_v2_opstype_config(); - let context = Context::new(test_proxy_controller); - let mocksrv = fakeserver.run(Testcases::ConfigVersionMismatchReassign(OSInstance::set_osi_nodestatus_config("openeuler", "default"))); - reconcile(os,context.clone()).await.expect("reconciler"); + let context = Context::new(test_proxy_controller); + let mocksrv = fakeserver.run(Testcases::ConfigVersionMismatchReassign(OSInstance::set_osi_nodestatus_config( + "openeuler", + "default", + ))); + reconcile(os, context.clone()).await.expect("reconciler"); timeout_after_5s(mocksrv).await; } #[tokio::test] - async fn test_sysconfig_version_mismatch_update(){ - let (test_proxy_controller,fakeserver) = ProxyController::::test(); + async fn test_sysconfig_version_mismatch_update() { + let (test_proxy_controller, fakeserver) = ProxyController::::test(); env::set_var("NODE_NAME", "openeuler"); let os = OS::set_os_syscon_v2_opstype_config(); - let context = Context::new(test_proxy_controller); - let mocksrv = fakeserver.run(Testcases::ConfigVersionMismatchUpdate(OSInstance::set_osi_nodestatus_upgrade("openeuler", "default"))); - reconcile(os,context.clone()).await.expect("reconciler"); + let context = Context::new(test_proxy_controller); + let mocksrv = fakeserver.run(Testcases::ConfigVersionMismatchUpdate(OSInstance::set_osi_nodestatus_upgrade( + "openeuler", + "default", + ))); + reconcile(os, context.clone()).await.expect("reconciler"); timeout_after_5s(mocksrv).await; } } diff --git a/KubeOS-Rust/proxy/src/controller/crd.rs b/KubeOS-Rust/proxy/src/controller/crd.rs index efec0bd8..41f333e8 100644 --- a/KubeOS-Rust/proxy/src/controller/crd.rs +++ b/KubeOS-Rust/proxy/src/controller/crd.rs @@ -23,6 +23,13 @@ pub struct OSSpec { pub containerimage: String, pub opstype: String, pub evictpodforce: bool, + pub imageurl: String, + #[serde(rename = "flagSafe")] + pub flagsafe: bool, + pub mtls: bool, + pub cacert: Option, + pub clientcert: Option, + pub clientkey: Option, pub sysconfigs: Option, pub upgradeconfigs: Option, } diff --git a/KubeOS-Rust/proxy/src/controller/mod.rs b/KubeOS-Rust/proxy/src/controller/mod.rs index b143082e..384d74b9 100644 --- a/KubeOS-Rust/proxy/src/controller/mod.rs +++ b/KubeOS-Rust/proxy/src/controller/mod.rs @@ -12,9 +12,9 @@ mod agentclient; mod apiclient; -mod controller; #[cfg(test)] mod apiserver_mock; +mod controller; mod crd; mod drain; mod utils; -- Gitee