Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented /v1/catalog/service/:service with strong types #39

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 42 additions & 14 deletions src/catalog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::collections::HashMap;
use crate::agent::{AgentCheck, AgentService};
use crate::errors::Result;
use crate::request::{get, put};
use crate::structs::*;
use crate::{Client, QueryMeta, QueryOptions, WriteMeta, WriteOptions};

#[serde(default)]
Expand All @@ -27,23 +28,34 @@ pub struct Node {

#[serde(default)]
#[derive(Eq, Default, PartialEq, Serialize, Deserialize, Debug)]
pub struct CatalogService {
ID: String,
Node: String,
Address: String,
pub struct NodeServiceProxy {
DestinationServiceName: String,
DestinationServiceID: String,
LocalServiceAddress: String,
LocalServicePort: u16,
}

#[serde(default)]
#[derive(Eq, Default, PartialEq, Serialize, Deserialize, Debug)]
pub struct CatalogNodeService {
ID: NodeID,
Node: NodeName,
Address: ConsulAddress,
Datacenter: String,
TaggedAddresses: HashMap<String, String>,
NodeMeta: HashMap<String, String>,
ServiceID: String,
ServiceName: String,
ServiceAddress: String,
ServiceTags: Vec<String>,
ServiceMeta: HashMap<String, String>,
ServicePort: u32,
ServiceWeights: Weights,
ServiceEnableTagOverride: bool,
pub TaggedAddresses: TaggedAddresses,
pub Meta: Metadata,
CreateIndex: u64,
ModifyIndex: u64,
ServiceAddress: ConsulAddress,
ServiceEnableTagOverride: bool,
pub ServiceID: ServiceID,
pub ServiceName: ServiceName,
pub ServicePort: OptionalServicePort,
ServiceMeta: Metadata,
ServiceTaggedAddresses: TaggedAddresses,
ServiceTags: ServiceTags,
ServiceProxy: Option<NodeServiceProxy>,
Namespace: Option<String>,
}

#[serde(default)]
Expand Down Expand Up @@ -90,6 +102,11 @@ pub trait Catalog {
) -> Result<((), WriteMeta)>;
fn datacenters(&self) -> Result<(Vec<String>, QueryMeta)>;
fn nodes(&self, q: Option<&QueryOptions>) -> Result<(Vec<Node>, QueryMeta)>;
fn nodes_for_service(
&self,
service: &ServiceName,
q: Option<&QueryOptions>,
) -> Result<(Vec<CatalogNodeService>, QueryMeta)>;
fn services(
&self,
q: Option<&QueryOptions>,
Expand Down Expand Up @@ -142,10 +159,21 @@ impl Catalog for Client {
get("/v1/catalog/nodes", &self.config, HashMap::new(), q)
}

// https://www.consul.io/api/catalog.html#list-services
fn services(
&self,
q: Option<&QueryOptions>,
) -> Result<(HashMap<String, Vec<String>>, QueryMeta)> {
get("/v1/catalog/services", &self.config, HashMap::new(), q)
}

// https://www.consul.io/api/catalog.html#list-nodes-for-service
fn nodes_for_service(
&self,
service: &ServiceName,
q: Option<&QueryOptions>,
) -> Result<(Vec<CatalogNodeService>, QueryMeta)> {
let path = format!("/v1/catalog/service/{}", service.to_str());
get(&path, &self.config, HashMap::new(), q)
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub mod errors;
pub mod health;
pub mod kv;
pub mod session;
pub mod structs;

mod request;

Expand Down
70 changes: 70 additions & 0 deletions src/structs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use std::error;
use std::fmt;

#[derive(Eq, Default, PartialEq, Serialize, Deserialize, Debug)]
pub struct ConsulAddress(String);

#[derive(Eq, Default, PartialEq, Serialize, Deserialize, Debug)]
pub struct ConsulID(String);

pub type ServiceID = ConsulID;
pub type NodeID = ConsulID;

#[derive(Eq, Default, PartialEq, Serialize, Deserialize, Debug)]
pub struct ConsulName(String);

pub type ServiceName = ConsulName;
pub type NodeName = ConsulName;

pub type ServicePort = u16;
pub type OptionalServicePort = Option<ServicePort>;
pub type ServiceTags = Vec<String>;

#[derive(Eq, Default, PartialEq, Serialize, Deserialize, Debug)]
pub struct Metadata(std::collections::HashMap<String, String>);

#[derive(Eq, Default, PartialEq, Serialize, Deserialize, Debug)]
pub struct TaggedAddresses(std::collections::HashMap<String, ConsulAddress>);

#[derive(Debug, Clone)]
pub struct InvalidName;

impl fmt::Display for InvalidName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "invalid first item to double")
}
}

// This is important for other errors to wrap this one.
impl error::Error for InvalidName {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
// Generic error, underlying cause isn't tracked.
None
}
}

impl ConsulID {
pub fn from(name: &str) -> std::result::Result<ConsulID, InvalidName> {
if name.contains("/") {
Err(InvalidName)
} else {
Ok(ConsulID(String::from(name)))
}
}
pub fn to_str(&self) -> &str {
&self.0.as_str()
}
}

impl ConsulName {
pub fn from(name: &str) -> std::result::Result<ConsulName, InvalidName> {
if name.contains("/") {
Err(InvalidName)
} else {
Ok(ConsulName(String::from(name)))
}
}
pub fn to_str(&self) -> &str {
&self.0.as_str()
}
}
24 changes: 24 additions & 0 deletions tests/catalog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,27 @@ fn ds_services_test() {
Some(val) => assert_eq!(val.len(), 0), // consul has no tags
}
}

#[test]
fn ds_nodes_tests() {
use consul::catalog::Catalog;
let config = Config::new().unwrap();
let client = Client::new(config);
let r = client.nodes(Option::None).unwrap();
assert_ne!(r.0.len(), 0);
}

#[test]
fn ds_nodes_for_service_tests() {
use crate::consul::structs::ServiceName;
use consul::catalog::Catalog;
let config = Config::new().unwrap();
let client = Client::new(config);
let service_name = ServiceName::from("consul").unwrap();
let r = client
.nodes_for_service(&service_name, Option::None)
.unwrap();
assert_ne!(r.0.len(), 0);
assert_eq!(r.0[0].ServiceName, service_name);
assert_eq!(r.0[0].ServicePort.unwrap(), 8300);
}