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

feat: internal-backstage endpoints are disableable. #565

Merged
merged 1 commit into from
Oct 16, 2024
Merged
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
21 changes: 21 additions & 0 deletions CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ This document contains the help content for the `unleash-edge` command-line prog
* `--token-header <TOKEN_HEADER>` — token header to use for edge authorization

Default value: `Authorization`
* `--disable-metrics-batch-endpoint` — Disables /internal-backstage/metricsbatch endpoint

This endpoint shows the current cached client metrics
* `--disable-metrics-endpoint` — Disables /internal-backstage/metrics endpoint

Typically used for prometheus scraping metrics.
* `--disable-features-endpoint` — Disables /internal-backstage/features endpoint

Used to show current cached features across environments
* `--disable-tokens-endpoint` — Disables /internal-backstage/tokens endpoint

Used to show tokens used to refresh feature caches, but also tokens already validated/invalidated against upstream



Expand Down Expand Up @@ -130,6 +142,7 @@ Run in edge mode
* `--redis-write-connection-timeout-milliseconds <REDIS_WRITE_CONNECTION_TIMEOUT_MILLISECONDS>` — Timeout (in milliseconds) for waiting for a successful connection to redis when persisting

Default value: `2000`
* `--s3-bucket-name <S3_BUCKET_NAME>` — Bucket name to use for storing feature and token data
* `--token-header <TOKEN_HEADER>` — Token header to use for both edge authorization and communication with the upstream server

Default value: `Authorization`
Expand All @@ -139,6 +152,13 @@ Run in edge mode
* `--dynamic` — If set to true, Edge starts with dynamic behavior. Dynamic behavior means that Edge will accept tokens outside the scope of the startup tokens

Default value: `false`
* `--prometheus-remote-write-url <PROMETHEUS_REMOTE_WRITE_URL>` — Sets a remote write url for prometheus metrics, if this is set, prometheus metrics will be written upstream
* `--prometheus-push-interval <PROMETHEUS_PUSH_INTERVAL>` — Sets the interval for prometheus push metrics, only relevant if `prometheus_remote_write_url` is set. Defaults to 60 seconds

Default value: `60`
* `--prometheus-username <PROMETHEUS_USERNAME>`
* `--prometheus-password <PROMETHEUS_PASSWORD>`
* `--prometheus-user-id <PROMETHEUS_USER_ID>`



Expand Down Expand Up @@ -196,3 +216,4 @@ Perform a ready check against a running edge instance
This document was generated automatically by
<a href="https://crates.io/crates/clap-markdown"><code>clap-markdown</code></a>.
</i></small>

5 changes: 2 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ opentelemetry_sdk = { version = "0.24.0", features = [
"serde_json",
"logs",
] }
prometheus = { version = "0.13.4", features = ["process", "push"] }
prometheus-reqwest-remote-write = { version = "0.1.1" }
prometheus = { version = "0.13.4", features = ["process"] }
prometheus-reqwest-remote-write = { version = "0.2.1" }
prometheus-static-metric = "0.5.1"
rand = "0.8.5"
redis = { version = "0.27.0", features = [
Expand Down
27 changes: 27 additions & 0 deletions server/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,30 @@ pub struct HealthCheckArgs {
pub ca_certificate_file: Option<PathBuf>,
}

#[derive(Args, Debug, Clone)]
pub struct InternalBackstageArgs {
/// Disables /internal-backstage/metricsbatch endpoint
///
/// This endpoint shows the current cached client metrics
#[clap(long, env, global = true)]
pub disable_metrics_batch_endpoint: bool,
/// Disables /internal-backstage/metrics endpoint
///
/// Typically used for prometheus scraping metrics.
#[clap(long, env, global = true)]
pub disable_metrics_endpoint: bool,
/// Disables /internal-backstage/features endpoint
///
/// Used to show current cached features across environments
#[clap(long, env, global = true)]
pub disable_features_endpoint: bool,
/// Disables /internal-backstage/tokens endpoint
///
/// Used to show tokens used to refresh feature caches, but also tokens already validated/invalidated against upstream
#[clap(long, env, global = true)]
pub disable_tokens_endpoint: bool,
}

#[derive(Args, Debug, Clone)]
pub struct TokenHeader {
/// Token header to use for edge authorization.
Expand Down Expand Up @@ -350,6 +374,9 @@ pub struct CliArgs {
/// token header to use for edge authorization.
#[clap(long, env, global = true, default_value = "Authorization")]
pub token_header: TokenHeader,

#[clap(flatten)]
pub internal_backstage: InternalBackstageArgs,
}

#[derive(Args, Debug, Clone)]
Expand Down
23 changes: 15 additions & 8 deletions server/src/internal_backstage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ use serde::{Deserialize, Serialize};
use unleash_types::client_features::ClientFeatures;
use unleash_types::client_metrics::ClientApplication;

use crate::auth::token_validator::TokenValidator;
use crate::error::EdgeError;
use crate::http::feature_refresher::FeatureRefresher;
use crate::metrics::actix_web_metrics::PrometheusMetricsHandler;
use crate::metrics::client_metrics::MetricsCache;
use crate::types::{BuildInfo, EdgeJsonResult, EdgeToken, TokenInfo, TokenRefresh};
use crate::types::{ClientMetric, MetricsInfo, Status};
use crate::{auth::token_validator::TokenValidator, cli::InternalBackstageArgs};

#[derive(Debug, Serialize, Deserialize)]
pub struct EdgeStatus {
Expand Down Expand Up @@ -131,14 +131,21 @@ pub async fn features(
pub fn configure_internal_backstage(
cfg: &mut web::ServiceConfig,
metrics_handler: PrometheusMetricsHandler,
internal_backtage_args: InternalBackstageArgs,
) {
cfg.service(health)
.service(info)
.service(tokens)
.service(ready)
.service(metrics_batch)
.service(web::resource("/metrics").route(web::get().to(metrics_handler)))
.service(features);
cfg.service(health).service(info).service(ready);
if !internal_backtage_args.disable_tokens_endpoint {
cfg.service(tokens);
}
if !internal_backtage_args.disable_metrics_endpoint {
cfg.service(web::resource("/metrics").route(web::get().to(metrics_handler)));
}
if !internal_backtage_args.disable_metrics_batch_endpoint {
cfg.service(metrics_batch);
}
if !internal_backtage_args.disable_features_endpoint {
cfg.service(features);
}
}

#[cfg(test)]
Expand Down
5 changes: 4 additions & 1 deletion server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ async fn main() -> Result<(), anyhow::Error> {
app_name: args.clone().app_name,
instance_id: args.clone().instance_id,
};
let app_name = args.app_name.clone();
let internal_backstage_args = args.internal_backstage.clone();
let (
(token_cache, features_cache, engine_cache),
token_validator,
Expand Down Expand Up @@ -114,6 +116,7 @@ async fn main() -> Result<(), anyhow::Error> {
internal_backstage::configure_internal_backstage(
service_cfg,
metrics_handler.clone(),
internal_backstage_args.clone(),
)
}))
.service(
Expand Down Expand Up @@ -169,7 +172,7 @@ async fn main() -> Result<(), anyhow::Error> {
_ = validator.schedule_revalidation_of_startup_tokens(edge.tokens, lazy_feature_refresher) => {
tracing::info!("Token validator validation of startup tokens was unexpectedly shut down");
}
_ = metrics_pusher::prometheus_remote_write(prom_registry_for_write, edge.prometheus_remote_write_url, edge.prometheus_push_interval, edge.prometheus_username, edge.prometheus_password) => {
_ = metrics_pusher::prometheus_remote_write(prom_registry_for_write, edge.prometheus_remote_write_url, edge.prometheus_push_interval, edge.prometheus_username, edge.prometheus_password, app_name) => {
tracing::info!("Prometheus push unexpectedly shut down");
}
}
Expand Down
21 changes: 16 additions & 5 deletions server/src/metrics/metrics_pusher.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use base64::Engine;
use prometheus_reqwest_remote_write::WriteRequest;
use reqwest::{header, Client};
use tracing::debug;

fn get_http_client(username: Option<String>, password: Option<String>) -> Client {
if let Some(uname) = username.clone() {
Expand Down Expand Up @@ -31,14 +32,15 @@ pub async fn prometheus_remote_write(
interval: u64,
username: Option<String>,
password: Option<String>,
app_name: String,
) {
let sleep_duration = tokio::time::Duration::from_secs(interval);
let client = get_http_client(username, password);
if let Some(address) = url {
loop {
tokio::select! {
_ = tokio::time::sleep(sleep_duration) => {
remote_write_prom(registry.clone(), address.clone(), client.clone()).await;
remote_write_prom(registry.clone(), address.clone(), client.clone(), app_name.clone()).await;
}
}
}
Expand All @@ -52,9 +54,17 @@ pub async fn prometheus_remote_write(
}
}

async fn remote_write_prom(registry: prometheus::Registry, url: String, client: reqwest::Client) {
let write_request = WriteRequest::from_metric_families(registry.gather())
.expect("Could not format write request");
async fn remote_write_prom(
registry: prometheus::Registry,
url: String,
client: reqwest::Client,
app_name: String,
) {
let write_request = WriteRequest::from_metric_families(
registry.gather(),
Some(vec![("app_name".into(), app_name)]),
)
.expect("Could not format write request");
let http_request = write_request
.build_http_request(client.clone(), &url, "unleash_edge")
.expect("Failed to build http request");
Expand All @@ -64,9 +74,10 @@ async fn remote_write_prom(registry: prometheus::Registry, url: String, client:
if !r.status().is_success() {
tracing::warn!("Prometheus push failed with status: {}", r.status());
}
debug!("Prometheus push successful");
}
Err(e) => {
tracing::warn!("Prometheus push failed with error: {}", e);
tracing::warn!("Prometheus push failed with error: {:?}", e);
}
}
}