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(config-endpoint): add initial implementation #1896

Merged
merged 11 commits into from Jul 11, 2023
5 changes: 4 additions & 1 deletion src/cmd/src/options.rs
Expand Up @@ -25,12 +25,15 @@ use crate::error::{LoadLayeredConfigSnafu, Result};
pub const ENV_VAR_SEP: &str = "__";
pub const ENV_LIST_SEP: &str = ",";

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub struct MixOptions {
pub fe_opts: FrontendOptions,
pub dn_opts: DatanodeOptions,
pub logging: LoggingOptions,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Options {
Datanode(Box<DatanodeOptions>),
Frontend(Box<FrontendOptions>),
Expand Down
1 change: 1 addition & 0 deletions src/frontend/Cargo.toml
Expand Up @@ -50,6 +50,7 @@ openmetrics-parser = "0.4"
partition = { path = "../partition" }
prost.workspace = true
query = { path = "../query" }
toml = "0.5"
regex = "1.6"
script = { path = "../script", features = ["python"], optional = true }
serde = "1.0"
Expand Down
5 changes: 5 additions & 0 deletions src/frontend/src/server.rs
Expand Up @@ -182,6 +182,7 @@ impl Services {
.with_metrics_handler(MetricsHandler)
.with_script_handler(instance.clone())
.with_configurator(plugins.get::<ConfiguratorRef>())
.with_greptime_config_options(parse_to_toml_string(opts))
.build();
result.push((Box::new(http_server), http_addr));
}
Expand All @@ -204,6 +205,10 @@ impl Services {
}
}

fn parse_to_toml_string(opts: &FrontendOptions) -> String {
toml::to_string(&opts).unwrap()
}

fn parse_addr(addr: &str) -> Result<SocketAddr> {
addr.parse().context(error::ParseAddrSnafu { addr })
}
Expand Down
28 changes: 27 additions & 1 deletion src/servers/src/http.rs
Expand Up @@ -124,6 +124,7 @@ pub struct HttpServer {
user_provider: Option<UserProviderRef>,
metrics_handler: Option<MetricsHandler>,
configurator: Option<ConfiguratorRef>,
greptime_config_options: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand Down Expand Up @@ -378,6 +379,11 @@ pub struct ApiState {
pub script_handler: Option<ScriptHandlerRef>,
}

#[derive(Clone)]
pub struct GreptimeOptionsConfigState {
pub greptime_config_options: String,
}

#[derive(Default)]
pub struct HttpServerBuilder {
inner: HttpServer,
Expand All @@ -398,6 +404,7 @@ impl HttpServerBuilder {
metrics_handler: None,
shutdown_tx: Mutex::new(None),
configurator: None,
greptime_config_options: None,
},
}
}
Expand Down Expand Up @@ -447,6 +454,11 @@ impl HttpServerBuilder {
self
}

pub fn with_greptime_config_options(&mut self, opts: String) -> &mut Self {
self.inner.greptime_config_options = Some(opts);
self
}

pub fn build(&mut self) -> HttpServer {
std::mem::take(self).inner
}
Expand Down Expand Up @@ -477,7 +489,7 @@ impl HttpServer {
script_handler: self.script_handler.clone(),
})
.finish_api(&mut api)
.layer(Extension(api));
.layer(Extension(api.clone()));
router = router.nest(&format!("/{HTTP_API_VERSION}"), sql_router);
}

Expand Down Expand Up @@ -518,6 +530,14 @@ impl HttpServer {
routing::get(handler::health).post(handler::health),
);

let config_router = self
.route_config(GreptimeOptionsConfigState {
greptime_config_options: self.greptime_config_options.clone().unwrap(),
})
.finish_api(&mut api);

router = router.nest(&format!("/{HTTP_API_VERSION}"), config_router);
MichaelScofield marked this conversation as resolved.
Show resolved Hide resolved

router = router.route("/status", routing::get(handler::status));

#[cfg(feature = "dashboard")]
Expand Down Expand Up @@ -629,6 +649,12 @@ impl HttpServer {
.route("/flush", routing::post(flush))
.with_state(grpc_handler)
}

fn route_config<S>(&self, state: GreptimeOptionsConfigState) -> ApiRouter<S> {
ApiRouter::new()
.route("/config", apirouting::get(handler::config))
.with_state(state)
}
}

/// A middleware to record metrics for HTTP.
Expand Down
9 changes: 8 additions & 1 deletion src/servers/src/http/handler.rs
Expand Up @@ -18,6 +18,7 @@ use std::time::Instant;

use aide::transform::TransformOperation;
use axum::extract::{Json, Query, State};
use axum::response::{IntoResponse, Response};
use axum::{Extension, Form};
use common_error::status_code::StatusCode;
use common_telemetry::{error, timer};
Expand All @@ -26,7 +27,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use session::context::UserInfo;

use crate::http::{ApiState, JsonResponse};
use crate::http::{ApiState, GreptimeOptionsConfigState, JsonResponse};
use crate::metrics::JEMALLOC_COLLECTOR;
use crate::metrics_handler::MetricsHandler;

Expand Down Expand Up @@ -184,3 +185,9 @@ pub async fn status() -> Json<StatusResponse<'static>> {
version: env!("CARGO_PKG_VERSION"),
})
}

/// Handler to expose configuration information info about runtime, build, etc.
#[axum_macros::debug_handler]
pub async fn config(State(state): State<GreptimeOptionsConfigState>) -> Response {
(axum::http::StatusCode::OK, state.greptime_config_options).into_response()
}
35 changes: 33 additions & 2 deletions src/servers/tests/http/http_handler_test.rs
Expand Up @@ -14,12 +14,17 @@

use std::collections::HashMap;

use axum::body::Body;
use axum::body::{Body, Bytes};
use axum::extract::{Json, Query, RawBody, State};
use axum::Form;
use common_telemetry::metric;
use http_body::combinators::UnsyncBoxBody;
use hyper::Response;
use metrics::counter;
use servers::http::{handler as http_handler, script as script_handler, ApiState, JsonOutput};
use servers::http::{
handler as http_handler, script as script_handler, ApiState, GreptimeOptionsConfigState,
JsonOutput,
};
use servers::metrics_handler::MetricsHandler;
use session::context::UserInfo;
use table::test_util::MemTable;
Expand Down Expand Up @@ -380,3 +385,29 @@ async fn test_status() {
let Json(json) = http_handler::status().await;
assert_eq!(json, expected_json);
}

#[tokio::test]
async fn test_config() {
let toml_str = r#"
mode = "distributed"

[http_options]
addr = "127.0.0.1:4000"
timeout = "30s"
body_limit = "2GB"

[logging]
level = "debug"
dir = "/tmp/greptimedb/test/logs"
"#;
let rs = http_handler::config(State(GreptimeOptionsConfigState {
greptime_config_options: toml_str.to_string(),
}))
.await;
assert_eq!(200_u16, rs.status().as_u16());
assert_eq!(get_body(rs).await, toml_str);
}

async fn get_body(response: Response<UnsyncBoxBody<Bytes, axum::Error>>) -> Bytes {
hyper::body::to_bytes(response.into_body()).await.unwrap()
}
2 changes: 2 additions & 0 deletions tests-integration/tests/http.rs
Expand Up @@ -513,3 +513,5 @@ pub async fn test_dashboard_path(store_type: StorageType) {

#[cfg(not(feature = "dashboard"))]
pub async fn test_dashboard_path(_: StorageType) {}

// TODO add status and config endpoints