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

Pull Admin module out of Metrics #220

Merged
merged 4 commits into from
Mar 29, 2021
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Not to be used in production systems.
* See the [proxy configuration reference](./docs/proxy-configuration.md) for all the configuration options.
* See the [Session documentation](./docs/session.md) for an overview of quilkin sessions and metrics.
* See [Filter documentation](./docs/extensions/filters/filters.md) for a list of filters, and their configuration options.
* The [Administration interface](./docs/admin.md) provides access to health and metrics endpoints.

## Code of Conduct

Expand Down
21 changes: 21 additions & 0 deletions docs/admin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Administration Interface

Quilkin exposes an HTTP interface to query different aspects of the server.

> It is assumed that the administration interface will only ever be able to be accessible on `localhost`.

By default, the administration interface is bound to `[::]:9091`, but it can be configured through the
[proxy configuration file](./proxy-configuration.md), like so:

```yaml
admin:
address: [::]:9095
```

The admin interface provides the following endpoints:

## /metrics

Outputs [Prometheus](https://prometheus.io/) formatted metrics for this proxy.

See the [Proxy Metrics](./proxy.md#metrics) documentation for what metrics are available.
4 changes: 4 additions & 0 deletions src/config/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ impl Builder {
Builder { source, ..self }
}

pub fn with_admin(self, admin: Admin) -> Self {
Self { admin, ..self }
}

pub fn build(self) -> Config {
Config {
version: Version::V1Alpha1,
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
mod cluster;
pub mod config;
pub mod extensions;
pub mod metrics;
pub(crate) mod metrics;
pub mod proxy;
pub mod runner;
pub mod test_utils;
Expand Down
83 changes: 83 additions & 0 deletions src/proxy/admin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2021 Google LLC All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

use std::convert::Infallible;
use std::net::SocketAddr;
use std::sync::Arc;

use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, Server as HyperServer, StatusCode};
use slog::{error, info, o, Logger};
use tokio::sync::watch;

use crate::proxy::Metrics;

pub struct Admin {
log: Logger,
/// The address that the Admin server starts on
addr: SocketAddr,
metrics: Arc<Metrics>,
}

impl Admin {
pub fn new(base: &Logger, addr: SocketAddr, metrics: Arc<Metrics>) -> Self {
Admin {
log: base.new(o!("source" => "proxy::Admin")),
addr,
metrics,
}
}

pub fn run(&self, mut shutdown_rx: watch::Receiver<()>) {
info!(self.log, "Starting admin endpoint"; "address" => self.addr.to_string());

let metrics = self.metrics.clone();
let make_svc = make_service_fn(move |_conn| {
let metrics = metrics.clone();
async move {
let metrics = metrics.clone();
Ok::<_, Infallible>(service_fn(move |req| {
let metrics = metrics.clone();
async move { Ok::<_, Infallible>(handle_request(req, metrics)) }
}))
}
});

let server = HyperServer::bind(&self.addr)
.serve(make_svc)
.with_graceful_shutdown(async move {
shutdown_rx.changed().await.ok();
});

let log = self.log.clone();
tokio::spawn(async move {
if let Err(err) = server.await {
error!(log, "Admin server exited with an error"; "error" => %err);
}
});
}
}

fn handle_request(request: Request<Body>, metrics: Arc<Metrics>) -> Response<Body> {
match (request.method(), request.uri().path()) {
(&Method::GET, "/metrics") => metrics.collect_metrics(),
(_, _) => {
let mut response = Response::new(Body::empty());
*response.status_mut() = StatusCode::NOT_FOUND;
response
}
}
}
47 changes: 31 additions & 16 deletions src/proxy/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,27 @@
* limitations under the License.
*/

use crate::cluster::Endpoint;
use crate::config::{
parse_endpoint_metadata_from_yaml, Admin, Config, Endpoints, ManagementServer, Proxy, Source,
ValidationError, ValueInvalidArgs, Version,
};
use crate::extensions::{default_registry, CreateFilterError, FilterChain, FilterRegistry};
use crate::proxy::server::metrics::Metrics as ProxyMetrics;
use crate::proxy::{Metrics, Server};
use slog::{o, Drain, Logger};
use std::collections::HashSet;
use std::convert::TryInto;
use std::marker::PhantomData;
use std::{
fmt::{self, Formatter},
sync::Arc,
};

use prometheus::Registry;
use slog::{o, Drain, Logger};
use tonic::transport::Endpoint as TonicEndpoint;

use crate::cluster::Endpoint;
use crate::config::{
parse_endpoint_metadata_from_yaml, Admin, Config, Endpoints, ManagementServer, Proxy, Source,
ValidationError, ValueInvalidArgs, Version,
};
use crate::extensions::{default_registry, CreateFilterError, FilterChain, FilterRegistry};
use crate::proxy::server::metrics::Metrics as ProxyMetrics;
use crate::proxy::{Admin as ProxyAdmin, Metrics, Server};

pub(super) enum ValidatedSource {
Static {
filter_chain: Arc<FilterChain>,
Expand Down Expand Up @@ -108,17 +111,21 @@ pub struct Builder<V> {
log: Logger,
config: Arc<Config>,
filter_registry: FilterRegistry,
metrics: Metrics,
admin: Option<ProxyAdmin>,
metrics: Arc<Metrics>,
validation_status: V,
}

impl From<Arc<Config>> for Builder<PendingValidation> {
fn from(config: Arc<Config>) -> Self {
let log = logger();
let metrics = Arc::new(Metrics::new(&log, Registry::default()));
let admin = ProxyAdmin::new(&log, config.admin.address, metrics.clone());
Builder {
config,
filter_registry: default_registry(&log),
metrics: Metrics::default(),
admin: Some(admin),
metrics,
log,
validation_status: PendingValidation,
}
Expand Down Expand Up @@ -249,8 +256,12 @@ impl Builder<PendingValidation> {
}
}

pub fn with_metrics(self, metrics: Metrics) -> Self {
Self { metrics, ..self }
/// Disable the admin interface
pub fn disable_admin(self) -> Self {
Self {
admin: None,
..self
}
}

// Validates the builder's config and filter configurations.
Expand All @@ -261,6 +272,7 @@ impl Builder<PendingValidation> {
Ok(Builder {
log: self.log,
config: self.config,
admin: self.admin,
metrics: self.metrics,
filter_registry: self.filter_registry,
validation_status: Validated(validated_config),
Expand All @@ -275,6 +287,7 @@ impl Builder<Validated> {
config: Arc::new(self.validation_status.0),
proxy_metrics: ProxyMetrics::new(&self.metrics.registry.clone())
.expect("metrics should be setup properly"),
admin: self.admin,
metrics: self.metrics,
filter_registry: Arc::new(self.filter_registry),
}
Expand All @@ -293,12 +306,14 @@ pub fn logger() -> Logger {

#[cfg(test)]
mod tests {
use super::{Builder, Error};
use crate::config::{Config, ValidationError};
use crate::proxy::builder::Validated;
use std::convert::TryFrom;
use std::sync::Arc;

use crate::config::{Config, ValidationError};
use crate::proxy::builder::Validated;

use super::{Builder, Error};

fn parse_config(yaml: &str) -> Config {
Config::from_reader(yaml.as_bytes()).unwrap()
}
Expand Down
Loading