- update: code update to use kube api instead of consul
This commit is contained in:
@@ -17,3 +17,5 @@ async-trait = "0.1.87"
|
||||
serde_json = "1.0.140"
|
||||
hickory-resolver = "0.24.4"
|
||||
rand = "0.8.5"
|
||||
kube = { version = "0.99.0", features = ["derive"] }
|
||||
k8s-openapi = { version = "0.24.0", features = ["v1_32"] }
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use hickory_resolver::config::*;
|
||||
use hickory_resolver::{Resolver, TokioAsyncResolver};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
use std::str::FromStr;
|
||||
use tokio::runtime::Runtime;
|
||||
use tracing::log::debug;
|
||||
use kube::{Client, Api};
|
||||
use k8s_openapi::api::core::v1::Service;
|
||||
use std::collections::{BTreeMap};
|
||||
use hickory_resolver::system_conf::read_system_conf;
|
||||
|
||||
pub async fn get_service_endpoints_by_dns(consul_url: &str, service_protocol: &str, service_name: &str) -> Result<Vec<SocketAddr>, Box<dyn std::error::Error>> {
|
||||
let mut rc = ResolverConfig::new();
|
||||
@@ -29,68 +29,92 @@ pub async fn get_service_endpoints_by_dns(consul_url: &str, service_protocol: &s
|
||||
Ok(endpoints)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ServiceNode {
|
||||
pub ServiceAddress: String,
|
||||
pub ServicePort: u16,
|
||||
pub ServiceTags: Vec<String>,
|
||||
pub ServiceMeta: HashMap<String, String>,
|
||||
pub async fn get_kube_service_endpoints_by_dns(port_name: &str, service_protocol: &str, service_name: &str) -> Result<Vec<SocketAddr>, Box<dyn std::error::Error>> {
|
||||
let (config, options) = read_system_conf()?;
|
||||
let resolver = TokioAsyncResolver::tokio(config, options);
|
||||
|
||||
let srv_name = format!("_{}._{}._{}", port_name, service_protocol, service_name);
|
||||
let srv_record = resolver.srv_lookup(&srv_name).await?;
|
||||
|
||||
let mut endpoints = Vec::new();
|
||||
for record in srv_record {
|
||||
let hostname = record.target();
|
||||
let lookup_responses = resolver.lookup_ip(hostname.to_string()).await?;
|
||||
for response in lookup_responses {
|
||||
endpoints.push(SocketAddr::from_str(&format!("{}:{}", &response.to_string(), record.port()))?);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(endpoints)
|
||||
}
|
||||
|
||||
pub async fn get_service_address(
|
||||
consul_url: &str,
|
||||
#[derive(Debug)]
|
||||
pub struct ServiceInfo {
|
||||
pub name: String,
|
||||
pub namespace: String,
|
||||
pub annotations: Option<BTreeMap<String, String>>,
|
||||
pub labels: Option<BTreeMap<String, String>>,
|
||||
}
|
||||
|
||||
pub async fn get_service_info(
|
||||
namespace: &str,
|
||||
service_name: &str,
|
||||
) -> Result<Vec<ServiceNode>, Box<dyn std::error::Error>> {
|
||||
let client = reqwest::Client::new();
|
||||
let consul_service_url = format!("{}/v1/catalog/service/{}", consul_url, service_name);
|
||||
) -> Result<ServiceInfo, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let client = Client::try_default().await?;
|
||||
|
||||
let response = client.get(&consul_service_url).send().await?;
|
||||
// Create an API object for services in the specified namespace
|
||||
let services: Api<Service> = Api::namespaced(client, namespace);
|
||||
|
||||
if !response.status().is_success() {
|
||||
return Err(format!(
|
||||
"Failed to fetch service nodes for '{}': {}",
|
||||
service_name,
|
||||
response.status()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
// Get the service object
|
||||
let service = services.get(service_name).await?;
|
||||
|
||||
// Deserialize the response into a Vec<ServiceNode>
|
||||
let nodes: Vec<ServiceNode> = response.json().await?;
|
||||
// Extract metadata
|
||||
let name = service.metadata.name.unwrap_or_default();
|
||||
let namespace = service.metadata.namespace.unwrap_or_default();
|
||||
let annotations = service.metadata.annotations.clone();
|
||||
let labels = service.metadata.labels.clone();
|
||||
|
||||
if nodes.is_empty() {
|
||||
Err(format!("No nodes found for service '{}'", service_name).into())
|
||||
} else {
|
||||
Ok(nodes)
|
||||
}
|
||||
// Return the service info
|
||||
Ok(ServiceInfo {
|
||||
name,
|
||||
namespace,
|
||||
annotations,
|
||||
labels,
|
||||
})
|
||||
}
|
||||
async fn get_services_with_tag(
|
||||
service_name: &str,
|
||||
tag: &str,
|
||||
consul_url: &str,
|
||||
) -> Result<Vec<ServiceNode>, Box<dyn std::error::Error>> {
|
||||
let url = format!("{}/v1/catalog/service/{}", consul_url, service_name);
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let response = client.get(&url).send().await?;
|
||||
pub async fn get_services_by_label(
|
||||
namespace: &str,
|
||||
label_selector: &str,
|
||||
) -> Result<Vec<ServiceInfo>, Box<dyn std::error::Error>> {
|
||||
let client = Client::try_default().await?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
return Err(format!(
|
||||
"Failed to fetch service nodes for '{}': {}",
|
||||
service_name,
|
||||
response.status()
|
||||
)
|
||||
.into());
|
||||
// Create an API object for services in the specified namespace
|
||||
let services: Api<Service> = Api::namespaced(client, namespace);
|
||||
|
||||
// Use ListParams to filter services by label
|
||||
let list_params = kube::api::ListParams::default().labels(label_selector);
|
||||
|
||||
// List services that match the label selector
|
||||
let service_list = services.list(&list_params).await?;
|
||||
|
||||
// Convert the list of services into a vector of ServiceInfo
|
||||
let mut service_infos = Vec::new();
|
||||
for service in service_list.items {
|
||||
let name = service.metadata.name.clone().unwrap_or_default();
|
||||
let namespace = service.metadata.namespace.clone().unwrap_or_default();
|
||||
|
||||
// Convert BTreeMap to HashMap for annotations and labels
|
||||
let annotations = service.metadata.annotations.map(|btree| btree.into_iter().collect());
|
||||
let labels = service.metadata.labels.map(|btree| btree.into_iter().collect());
|
||||
|
||||
service_infos.push(ServiceInfo {
|
||||
name,
|
||||
namespace,
|
||||
annotations,
|
||||
labels,
|
||||
});
|
||||
}
|
||||
|
||||
// Deserialize the response into a Vec<ServiceNode>
|
||||
let nodes: Vec<ServiceNode> = response.json().await?;
|
||||
|
||||
// Filter nodes that include the specified tag
|
||||
let filtered_nodes = nodes
|
||||
.into_iter()
|
||||
.filter(|node| node.ServiceTags.contains(&tag.to_string()))
|
||||
.collect();
|
||||
|
||||
Ok(filtered_nodes)
|
||||
Ok(service_infos)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user