Skip to content

Commit

Permalink
Configuration: allow custom health check endpoint
Browse files Browse the repository at this point in the history
resolves apollographql#2938

Adds a configuration option for custom health check endpoints, using
`/health` as default
  • Loading branch information
Aaron Arinder authored and aaronArinder committed Nov 6, 2023
1 parent 65f97e1 commit 4ff6c68
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 11 deletions.
9 changes: 9 additions & 0 deletions .changesets/config_aaron_allow_custom_healthcheck_endpoint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
### Configuration: allow custom health check endpoint ([Issue #2938](https://github.com/apollographql/router/issues/2938))

Adds a configuration option for custom health check endpoints, using `/health` as default


<!-- start metadata -->
---

By [@aaronArinder](https://github.com/aaronArinder) in https://github.com/apollographql/router/pull/4145
3 changes: 3 additions & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ The CI checks require `cargo-deny` and `cargo-about` which can both be installed
- `cargo install cargo-deny`
- `cargo install cargo-about`

Updating the snapshots used during testing requires installing `cargo-insta`:
- `cargo install cargo-insta`

They also need you to have the federation-demo project up and running,
as explained in the Getting started section above.

Expand Down
7 changes: 4 additions & 3 deletions apollo-router/src/axum_factory/axum_http_server_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,14 @@ where

if configuration.health_check.enabled {
tracing::info!(
"Health check endpoint exposed at {}/health",
configuration.health_check.listen
"Health check exposed at {}/{}",
configuration.health_check.listen,
configuration.health_check.path
);
endpoints.insert(
configuration.health_check.listen.clone(),
Endpoint::from_router_service(
"/health".to_string(),
configuration.health_check.path.clone(),
service_fn(move |req: router::Request| {
let mut status_code = StatusCode::OK;
let health = if let Some(query) = req.router_request.uri().query() {
Expand Down
40 changes: 34 additions & 6 deletions apollo-router/src/configuration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1161,25 +1161,43 @@ pub(crate) struct HealthCheck {
/// Defaults to 127.0.0.1:8088
pub(crate) listen: ListenAddr,

/// Set to false to disable the health check endpoint
/// Set to false to disable the health check
pub(crate) enabled: bool,

/// Optionally set a custom healthcheck path
/// Defaults to /health
pub(crate) path: String,
}

fn default_health_check_listen() -> ListenAddr {
SocketAddr::from_str("127.0.0.1:8088").unwrap().into()
}

fn default_health_check() -> bool {
fn default_health_check_enabled() -> bool {
true
}

fn default_health_check_path() -> String {
"/health".to_string()
}

#[buildstructor::buildstructor]
impl HealthCheck {
#[builder]
pub(crate) fn new(listen: Option<ListenAddr>, enabled: Option<bool>) -> Self {
pub(crate) fn new(
listen: Option<ListenAddr>,
enabled: Option<bool>,
path: Option<String>,
) -> Self {
let mut path = path.unwrap_or_else(default_health_check_path);
if !path.starts_with('/') {
path = format!("/{path}").to_string();
}

Self {
listen: listen.unwrap_or_else(default_health_check_listen),
enabled: enabled.unwrap_or_else(default_health_check),
enabled: enabled.unwrap_or_else(default_health_check_enabled),
path,
}
}
}
Expand All @@ -1188,10 +1206,20 @@ impl HealthCheck {
#[buildstructor::buildstructor]
impl HealthCheck {
#[builder]
pub(crate) fn fake_new(listen: Option<ListenAddr>, enabled: Option<bool>) -> Self {
pub(crate) fn fake_new(
listen: Option<ListenAddr>,
enabled: Option<bool>,
path: Option<String>,
) -> Self {
let mut path = path.unwrap_or_else(default_health_check_path);
if !path.starts_with('/') {
path = format!("/{path}").to_string();
}

Self {
listen: listen.unwrap_or_else(test_listen),
enabled: enabled.unwrap_or_else(default_health_check),
enabled: enabled.unwrap_or_else(default_health_check_enabled),
path,
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1557,12 +1557,13 @@ expression: "&schema"
"description": "Health check configuration",
"default": {
"listen": "127.0.0.1:8088",
"enabled": true
"enabled": true,
"path": "/health"
},
"type": "object",
"properties": {
"enabled": {
"description": "Set to false to disable the health check endpoint",
"description": "Set to false to disable the health check",
"default": true,
"type": "boolean"
},
Expand All @@ -1579,6 +1580,11 @@ expression: "&schema"
"type": "string"
}
]
},
"path": {
"description": "Optionally set a custom healthcheck path Defaults to /health",
"default": "/health",
"type": "string"
}
},
"additionalProperties": false
Expand Down
33 changes: 33 additions & 0 deletions apollo-router/src/configuration/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,39 @@ fn test_deserialize_derive_default() {
}
}

#[test]
fn it_defaults_health_check_configuration() {
let conf = Configuration::default();
let addr: ListenAddr = SocketAddr::from_str("127.0.0.1:8088").unwrap().into();

assert_eq!(conf.health_check.listen, addr);
assert_eq!(&conf.health_check.path, "/health");

// Defaults to enabled: true
assert!(conf.health_check.enabled);
}

#[test]
fn it_sets_custom_health_check_path() {
let conf = Configuration::builder()
.health_check(HealthCheck::new(None, None, Some("/healthz".to_string())))
.build()
.unwrap();

assert_eq!(&conf.health_check.path, "/healthz");
}

#[test]
fn it_adds_slash_to_custom_health_check_path_if_missing() {
let conf = Configuration::builder()
// NB the missing `/`
.health_check(HealthCheck::new(None, None, Some("healthz".to_string())))
.build()
.unwrap();

assert_eq!(&conf.health_check.path, "/healthz");
}

fn has_field_level_serde_defaults(lines: &[&str], line_number: usize) -> bool {
let serde_field_default = Regex::new(
r#"^\s*#[\s\n]*\[serde\s*\((.*,)?\s*default\s*=\s*"[a-zA-Z0-9_:]+"\s*(,.*)?\)\s*\]\s*$"#,
Expand Down
17 changes: 17 additions & 0 deletions docs/source/configuration/health-checks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ You can change this by setting `health_check`:
health_check:
listen: 127.0.0.1:8088
enabled: true
path: /health
```
Each option is configurable. For example, we might want to set our health check to `127.0.0.1:8090/healthz`:

```yaml title="router.yaml"
health_check:
listen: 127.0.0.1:8090
enabled: true
path: /healthz
```

We can also disable the health check:

```yaml title="router.yaml"
health_check:
enabled: false
```

## Testing with `curl`
Expand Down

0 comments on commit 4ff6c68

Please sign in to comment.