From 921c9048184fb9573d10f3cf99a8b9a1c435f402 Mon Sep 17 00:00:00 2001 From: Sam Judd Date: Tue, 31 Oct 2023 13:36:11 -0700 Subject: [PATCH 1/3] Add support for deregister_service --- src/lib.rs | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/types.rs | 22 ++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 46259ec..eacfbfc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,6 +143,7 @@ const CREATE_OR_UPDATE_KEY_SYNC_METHOD_NAME: &str = "create_or_update_key_sync"; const DELETE_KEY_METHOD_NAME: &str = "delete_key"; const GET_LOCK_METHOD_NAME: &str = "get_lock"; const REGISTER_ENTITY_METHOD_NAME: &str = "register_entity"; +const DEREGISTER_ENTITY_METHOD_NAME: &str = "deregister_entity"; const GET_ALL_REGISTERED_SERVICE_NAMES_METHOD_NAME: &str = "get_all_registered_service_names"; const GET_SERVICE_NODES_METHOD_NAME: &str = "get_service_nodes"; const GET_SESSION_METHOD_NAME: &str = "get_session"; @@ -491,6 +492,28 @@ impl Consul { Ok(()) } + /// Removes entries from consul's global catalog. + /// See https://www.consul.io/api-docs/catalog#deregister-entity for more information. + /// # Arguments: + /// - payload: The [`DeregisterEntityPayload`](DeregisterEntityPayload) to provide the register entity API. + /// # Errors: + /// [ConsulError](consul::ConsulError) describes all possible errors returned by this api. + pub async fn deregister_entity( + &self, + payload: &DeregisterEntityPayload) -> Result<()> { + let uri = format!("{}/v1/catalog/deregister", self.config.address); + let request = hyper::Request::builder().method(Method::PUT).uri(uri); + let payload = serde_json::to_string(payload).map_err(ConsulError::InvalidRequest)?; + self.execute_request( + request, + payload.into(), + Some(Duration::from_secs(5)), + DEREGISTER_ENTITY_METHOD_NAME, + ) + .await?; + Ok(()) + } + /// Returns all services currently registered with consul. /// See https://www.consul.io/api-docs/catalog#list-services for more information. /// # Arguments: @@ -947,6 +970,69 @@ mod tests { assert!(service_names_after_register.contains(&new_service_name)); } + #[tokio::test(flavor = "multi_thread")] + async fn test_deregister_and_retrieve_services() { + let consul = get_client(); + + let new_service_name = "test-service-44".to_string(); + + // verify a service by this name is currently not registered + let ResponseMeta { + response: service_names_before_register, + .. + } = consul + .get_all_registered_service_names(None) + .await + .expect("expected get_registered_service_names request to succeed"); + assert!(!service_names_before_register.contains(&new_service_name)); + + let node = "local".to_string(); + + // register a new service + let payload = RegisterEntityPayload { + ID: None, + Node: node.clone(), + Address: "127.0.0.1".to_string(), + Datacenter: None, + TaggedAddresses: Default::default(), + NodeMeta: Default::default(), + Service: Some(RegisterEntityService { + ID: None, + Service: new_service_name.clone(), + Tags: vec![], + TaggedAddresses: Default::default(), + Meta: Default::default(), + Port: Some(42424), + Namespace: None, + }), + Check: None, + SkipNodeUpdate: None, + }; + consul + .register_entity(&payload) + .await + .expect("expected register_entity request to succeed"); + let payload = DeregisterEntityPayload { + Node: Some(node), + Datacenter: None, + CheckID: None, + ServiceID: None, + Namespace: None, + }; + consul.deregister_entity(&payload) + .await + .expect("expected deregister_entity request to succeed"); + // verify the newly registered service is retrieved + let ResponseMeta { + response: service_names_after_register, + .. + } = consul + .get_all_registered_service_names(None) + .await + .expect("expected get_registered_service_names request to succeed"); + assert!(!service_names_after_register.contains(&new_service_name)); + } + #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn get_services_nodes() { let consul = get_client(); diff --git a/src/types.rs b/src/types.rs index b78e0c4..5159da0 100644 --- a/src/types.rs +++ b/src/types.rs @@ -320,6 +320,28 @@ pub struct RegisterEntityPayload { pub SkipNodeUpdate: Option, } +/// The service to deregister with consul's global catalog. +/// See https://www.consul.io/api/agent/service for more information. +#[allow(non_snake_case)] +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct DeregisterEntityPayload { + /// The node to execute the check on. + #[serde(skip_serializing_if = "Option::is_none")] + pub Node: Option, + /// The datacenter to register in, defaults to the agent's datacenter. + #[serde(skip_serializing_if = "Option::is_none")] + pub Datacenter: Option, + /// Specifies the ID of the check to remove. + #[serde(skip_serializing_if = "Option::is_none")] + pub CheckID: Option, + /// Specifies the ID of the service to remove. The service and all associated checks will be removed. + #[serde(skip_serializing_if = "Option::is_none")] + pub ServiceID: Option, + /// Specifies the namespace of the service and checks you deregister. + #[serde(skip_serializing_if = "Option::is_none")] + pub Namespace: Option, +} + /// The service to register with consul's global catalog. /// See https://www.consul.io/api/agent/service for more information. #[allow(non_snake_case)] From edc6ac559c01360f4728367951c527f9b62c5d4a Mon Sep 17 00:00:00 2001 From: Sam Judd Date: Tue, 31 Oct 2023 13:49:51 -0700 Subject: [PATCH 2/3] Pull out is_registered/register_service methods --- src/lib.rs | 145 ++++++++++++++++++++--------------------------------- 1 file changed, 54 insertions(+), 91 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index eacfbfc..17d43da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -923,97 +923,21 @@ mod tests { let consul = get_client(); let new_service_name = "test-service-44".to_string(); + register_entity(&consul, &new_service_name, "local").await; - // verify a service by this name is currently not registered - let ResponseMeta { - response: service_names_before_register, - .. - } = consul - .get_all_registered_service_names(None) - .await - .expect("expected get_registered_service_names request to succeed"); - assert!(!service_names_before_register.contains(&new_service_name)); - - // register a new service - let payload = RegisterEntityPayload { - ID: None, - Node: "local".to_string(), - Address: "127.0.0.1".to_string(), - Datacenter: None, - TaggedAddresses: Default::default(), - NodeMeta: Default::default(), - Service: Some(RegisterEntityService { - ID: None, - Service: new_service_name.clone(), - Tags: vec![], - TaggedAddresses: Default::default(), - Meta: Default::default(), - Port: Some(42424), - Namespace: None, - }), - Check: None, - SkipNodeUpdate: None, - }; - consul - .register_entity(&payload) - .await - .expect("expected register_entity request to succeed"); - - // verify the newly registered service is retrieved - let ResponseMeta { - response: service_names_after_register, - .. - } = consul - .get_all_registered_service_names(None) - .await - .expect("expected get_registered_service_names request to succeed"); - assert!(service_names_after_register.contains(&new_service_name)); + assert!(is_registered(&consul, &new_service_name).await); } #[tokio::test(flavor = "multi_thread")] async fn test_deregister_and_retrieve_services() { let consul = get_client(); - let new_service_name = "test-service-44".to_string(); + let new_service_name = "test-service-45".to_string(); + let node_id = "local"; + register_entity(&consul, &new_service_name, node_id).await; - // verify a service by this name is currently not registered - let ResponseMeta { - response: service_names_before_register, - .. - } = consul - .get_all_registered_service_names(None) - .await - .expect("expected get_registered_service_names request to succeed"); - assert!(!service_names_before_register.contains(&new_service_name)); - - let node = "local".to_string(); - - // register a new service - let payload = RegisterEntityPayload { - ID: None, - Node: node.clone(), - Address: "127.0.0.1".to_string(), - Datacenter: None, - TaggedAddresses: Default::default(), - NodeMeta: Default::default(), - Service: Some(RegisterEntityService { - ID: None, - Service: new_service_name.clone(), - Tags: vec![], - TaggedAddresses: Default::default(), - Meta: Default::default(), - Port: Some(42424), - Namespace: None, - }), - Check: None, - SkipNodeUpdate: None, - }; - consul - .register_entity(&payload) - .await - .expect("expected register_entity request to succeed"); let payload = DeregisterEntityPayload { - Node: Some(node), + Node: Some(node_id.to_string()), Datacenter: None, CheckID: None, ServiceID: None, @@ -1022,15 +946,8 @@ mod tests { consul.deregister_entity(&payload) .await .expect("expected deregister_entity request to succeed"); - // verify the newly registered service is retrieved - let ResponseMeta { - response: service_names_after_register, - .. - } = consul - .get_all_registered_service_names(None) - .await - .expect("expected get_registered_service_names request to succeed"); - assert!(!service_names_after_register.contains(&new_service_name)); + + assert!(!is_registered(&consul, &new_service_name).await); } #[tokio::test(flavor = "multi_thread", worker_threads = 4)] @@ -1296,6 +1213,52 @@ mod tests { assert_ne!(mod_idx3, mod_idx4); } + async fn register_entity(consul: &Consul, service_name: &String, node_id: &str) { + let ResponseMeta { + response: service_names_before_register, + .. + } = consul + .get_all_registered_service_names(None) + .await + .expect("expected get_registered_service_names request to succeed"); + assert!(!service_names_before_register.contains(service_name)); + + let payload = RegisterEntityPayload { + ID: None, + Node: node_id.to_string(), + Address: "127.0.0.1".to_string(), + Datacenter: None, + TaggedAddresses: Default::default(), + NodeMeta: Default::default(), + Service: Some(RegisterEntityService { + ID: None, + Service: service_name.clone(), + Tags: vec![], + TaggedAddresses: Default::default(), + Meta: Default::default(), + Port: Some(42424), + Namespace: None, + }), + Check: None, + SkipNodeUpdate: None, + }; + consul + .register_entity(&payload) + .await + .expect("expected register_entity request to succeed"); + } + + async fn is_registered(consul: &Consul, service_name: &String) -> bool { + let ResponseMeta { + response: service_names_after_register, + .. + } = consul + .get_all_registered_service_names(None) + .await + .expect("expected get_registered_service_names request to succeed"); + service_names_after_register.contains(service_name) + } + fn get_client() -> Consul { let conf: Config = Config::from_env(); Consul::new(conf) From 12bb8f21b22ffe9c7239c497fd568055404bb28e Mon Sep 17 00:00:00 2001 From: Sam Judd Date: Tue, 31 Oct 2023 13:51:27 -0700 Subject: [PATCH 3/3] Reformat --- src/lib.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 17d43da..3514fff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -498,9 +498,7 @@ impl Consul { /// - payload: The [`DeregisterEntityPayload`](DeregisterEntityPayload) to provide the register entity API. /// # Errors: /// [ConsulError](consul::ConsulError) describes all possible errors returned by this api. - pub async fn deregister_entity( - &self, - payload: &DeregisterEntityPayload) -> Result<()> { + pub async fn deregister_entity(&self, payload: &DeregisterEntityPayload) -> Result<()> { let uri = format!("{}/v1/catalog/deregister", self.config.address); let request = hyper::Request::builder().method(Method::PUT).uri(uri); let payload = serde_json::to_string(payload).map_err(ConsulError::InvalidRequest)?; @@ -936,14 +934,15 @@ mod tests { let node_id = "local"; register_entity(&consul, &new_service_name, node_id).await; - let payload = DeregisterEntityPayload { - Node: Some(node_id.to_string()), - Datacenter: None, - CheckID: None, - ServiceID: None, - Namespace: None, + let payload = DeregisterEntityPayload { + Node: Some(node_id.to_string()), + Datacenter: None, + CheckID: None, + ServiceID: None, + Namespace: None, }; - consul.deregister_entity(&payload) + consul + .deregister_entity(&payload) .await .expect("expected deregister_entity request to succeed"); @@ -1213,7 +1212,7 @@ mod tests { assert_ne!(mod_idx3, mod_idx4); } - async fn register_entity(consul: &Consul, service_name: &String, node_id: &str) { + async fn register_entity(consul: &Consul, service_name: &String, node_id: &str) { let ResponseMeta { response: service_names_before_register, ..