feature: Register consul services with agent API #567
No reviewers
Labels
No labels
action
check-aws
action
discussion-needed
action
for-external-contributors
action
for-newcomers
action
more-info-needed
action
need-funding
action
triage-required
kind
correctness
kind
ideas
kind
improvement
kind
performance
kind
testing
kind
usability
kind
wrong-behavior
prio
critical
prio
low
scope
admin-api
scope
background-healing
scope
build
scope
documentation
scope
k8s
scope
layout
scope
metadata
scope
ops
scope
rpc
scope
s3-api
scope
security
scope
telemetry
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: Deuxfleurs/garage#567
Loading…
Reference in a new issue
No description provided.
Delete branch "unrob/garage:roberto/consul-agent-registration"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements service registration against the Consul agent API as a new feature, complementary to the current catalog API based one.
This allows the RPC service to be registered as a consul service (with additional tags/service metadata, if any are configured) through the higher level API through plain old TLS (not mTLS) and an optional ACL token. I've been running this at home for a few days now and really happy with it.
The necessary consul ACL policy looks like:
This is my first time using rust and/or nix so I'm sure there's stuff I've missed, happy to continue working on it and would appreciate any feedback!
This is interesting but it looks to me like a lot of duplicate code:
there is no need for a new feature flag, consul service discovery should just be a different mode for consul discovery than the default one
there is no need to duplicate the configuration options, just add a
mode = "node|service"
option to the existing[consul_discovery]
section (and add to that section any new parameters that are required by the service discovery mode)a lot of the definitions in
consul_service.rs
are the same as inconsul.rs
, please merge both files (your changes should just consist in adding the smallest possible number of struct declarations and new methods in theconsul.rs
file)we now have two versions of the
advertise_to_consul
function, both of which are incompatible because they have the same name (meaning garage can't build with both feature flags activated). Please merge these two functions into one, and add the logic there to distinguish between the case of regular discovery as we had before and service discovery you are addingYour final patch should be about 25% the size of this one.
Thanks for the thorough feedback Alex!
Not sure I can make it 1/4th of the original size—I did briefly explore separating the catalog/agent logic into traits but that seemed to make the patch larger. Is there a particular approach to expressing these differences more succinctly you had in mind? As I mentioned, this is my first time writing rust so any examples or inline comments would be great.
Went with the separate feature in order to ensure I wasn't affecting the original implementation; an approach I could think of, but deemed too aggressive, was to delete the
/v1/catalog
-based implementation, only keeping the option for mTLS (I personally disableverify_incoming
on thehttps
listener for convenience, but perhaps other folks don't). That looks like this,+118/-80
, but still not 1/4th of the PR's size.I named the switch
consul_http_api
as I thought that maps best to what it toggles, and named the "modes"catalog
andagent
accordingly.Let me know what you think!
This new version looks very good! The 25% size target was an exageration but you get the idea. Indeed, using traits is not always the best solution, and in this case I think it would have been huge overkill.
I've made a few remarks for simplifying the code. I'm suggesting to also add
META_PREFIX
to the old code for consistency/simplicity and because it's a good practice to have, even if it will break things when Garage daemons of different versions are runing together (we're still beta so that's fine).@ -35,12 +35,18 @@ bootstrap_peers = [
[consul_discovery]
mode = "node"
Is this the old name of
consul_http_api
?I'd suggest calling this parameter
api
, as it is already under the[consul_discovery]
section so there is no need to specify that it is related to Consul.For all parameters: take care of making sure the names in the docs are consistent with the actual code
@ -41,2 +42,4 @@
client_cert = "/etc/consul/consul-client.crt"
client_key = "/etc/consul/consul-key.crt"
# for `service` mode, unset client_cert and client_key, and optionally enable `consul_http_token`
# consul_http_token = "abcdef-01234-56789"
Similarly, I'd suggest calling this simply
token
@ -41,2 +46,4 @@
#[serde(rename = "Tags")]
tags: Vec<String>,
#[serde(rename = "Meta")]
service_meta: HashMap<String, String>,
It's called
service_meta
here butmeta
in the other definition below. Let's just call itmeta
everywhere@ -87,3 +80,1 @@
.use_rustls_tls()
.identity(identity)
.build()?
let mut builder: reqwest::ClientBuilder = reqwest::Client::builder();
add
.use_rustls_tls()
here and remove all other instances below (we are using rustls in all cases)@ -90,1 +109,4 @@
_ => return Err(ConsulError::InvalidTLSConfig),
}
builder.build()?
refactoring: move
builder.build()?
after the matchThis means that there is no
let client = match
, just a match block that mutatesbuilder
and after the match block there is
let client = builder.build()?;
@ -118,0 +145,4 @@
let pubkey = match &self.config.consul_http_api {
ConsulDiscoveryAPI::Catalog => ent.node_meta.get("pubkey"),
ConsulDiscoveryAPI::Agent => {
ent.service_meta.get(&format!("{}-pubkey", META_PREFIX))
I think we should also add the
META_PREFIX
to the old code that uses the Catalog API, it's a good practice to have and it will simplify the code. It will make Consul discovery incompatible between versions but that's fine, we are still beta.@ -141,0 +182,4 @@
let meta_prefix: String = match &self.config.consul_http_api {
ConsulDiscoveryAPI::Catalog => "".to_string(),
ConsulDiscoveryAPI::Agent => format!("{}-", META_PREFIX),
};
this can also be simplified by using META_PREFIX in all cases
@ -141,0 +193,4 @@
for (key, value) in global_meta.into_iter() {
meta.insert(key.clone(), value.clone());
}
}
I'd replace 187-195 by the following:
@ -162,2 +229,2 @@
let http = self.client.put(&url).json(&advertisement).send().await?;
}),
})
I don't like the big
(
...)
around the match. Can youlet
the result of the match to a separate variablereq_json
, and then dolet http = req_json.send().await?;
Went with an untagged enum here, since I couldn't think of a type for the advertisement structs; thought I could use
serde::Serialize
but that didn't work out and I'm still reading up to understand why.@ -137,1 +137,4 @@
#[derive(Deserialize, Debug, Clone)]
pub enum ConsulDiscoveryAPI {
#[serde(rename_all = "lowercase")]
I think the
rename_all
line has to go above thepub enum ConsulDiscoveryAPI
line?@ -138,0 +141,4 @@
Catalog,
Agent,
}
impl ConsulDiscoveryAPI {
This whole impl can be avoided by deriving
Default
on the enum. The default variant has to be marked with a#[default]
tag.Really appreciate your comments, gave it another go and will be deploying a build of this at home to double check. Will report back later this week.
Been running this build for a few days and things are looking good, missed a default for the
api
config key, but I think it's ready for review.LGTM, just some small inconsistencies in the documentation
@ -40,3 +41,4 @@
ca_cert = "/etc/consul/consul-ca.crt"
client_cert = "/etc/consul/consul-client.crt"
client_key = "/etc/consul/consul-key.crt"
# for `catalog` API mode, unset client_cert and client_key, and optionally enable `token`
I think you meant for
agent
API mode instead?@ -319,0 +326,4 @@
Two APIs for service registration are supported: `catalog` and `agent`. `catalog`, the default, will register a service using
the `/v1/catalog` endpoints, enabling mTLS if `client_cert` and `client_key` are provided. The `agent` API uses the
`v1/agent` endpoints instead, where an optional `consul_http_token` may be provided.
token
instead ofconsul_http_token
e8af3a7144
to32ad4538ee
Thanks for the work, I'll merge now, sorry for the wait!