-
Notifications
You must be signed in to change notification settings - Fork 40
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
[reconfigurator] clickhouse-admin
SMF service with dropshot server
#6304
Changes from all commits
b3d31c7
45f8ae5
9e424e5
ddb45a5
f6d2994
cb8e98d
a487b63
f2ba87e
5097bfb
eeab72c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
[package] | ||
name = "omicron-clickhouse-admin" | ||
version = "0.1.0" | ||
edition = "2021" | ||
license = "MPL-2.0" | ||
|
||
[dependencies] | ||
anyhow.workspace = true | ||
camino.workspace = true | ||
chrono.workspace = true | ||
clap.workspace = true | ||
clickhouse-admin-api.workspace = true | ||
dropshot.workspace = true | ||
http.workspace = true | ||
illumos-utils.workspace = true | ||
omicron-common.workspace = true | ||
omicron-uuid-kinds.workspace = true | ||
schemars.workspace = true | ||
slog.workspace = true | ||
slog-async.workspace = true | ||
slog-dtrace.workspace = true | ||
slog-error-chain.workspace = true | ||
serde.workspace = true | ||
thiserror.workspace = true | ||
tokio.workspace = true | ||
tokio-postgres.workspace = true | ||
toml.workspace = true | ||
|
||
omicron-workspace-hack.workspace = true | ||
|
||
[dev-dependencies] | ||
expectorate.workspace = true | ||
nexus-test-utils.workspace = true | ||
omicron-test-utils.workspace = true | ||
openapi-lint.workspace = true | ||
openapiv3.workspace = true | ||
serde_json.workspace = true | ||
subprocess.workspace = true | ||
url.workspace = true | ||
|
||
[lints] | ||
workspace = true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
[package] | ||
name = "clickhouse-admin-api" | ||
version = "0.1.0" | ||
edition = "2021" | ||
license = "MPL-2.0" | ||
|
||
[lints] | ||
workspace = true | ||
|
||
[dependencies] | ||
dropshot.workspace = true | ||
omicron-common.workspace = true | ||
omicron-uuid-kinds.workspace = true | ||
omicron-workspace-hack.workspace = true | ||
schemars.workspace = true | ||
serde.workspace = true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// This Source Code Form is subject to the terms of the Mozilla Public | ||
// License, v. 2.0. If a copy of the MPL was not distributed with this | ||
// file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
||
use dropshot::{HttpError, HttpResponseOk, RequestContext}; | ||
use schemars::JsonSchema; | ||
use serde::{Deserialize, Serialize}; | ||
use std::net::SocketAddrV6; | ||
|
||
#[dropshot::api_description] | ||
pub trait ClickhouseAdminApi { | ||
type Context; | ||
|
||
/// Retrieve the address the ClickHouse server or keeper node is listening on | ||
#[endpoint { | ||
method = GET, | ||
path = "/node/address", | ||
}] | ||
async fn clickhouse_address( | ||
rqctx: RequestContext<Self::Context>, | ||
) -> Result<HttpResponseOk<ClickhouseAddress>, HttpError>; | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, JsonSchema)] | ||
#[serde(rename_all = "snake_case")] | ||
pub struct ClickhouseAddress { | ||
pub clickhouse_address: SocketAddrV6, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// This Source Code Form is subject to the terms of the Mozilla Public | ||
// License, v. 2.0. If a copy of the MPL was not distributed with this | ||
// file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
||
//! Executable program to run the Omicron ClickHouse admin interface | ||
|
||
use anyhow::anyhow; | ||
use camino::Utf8PathBuf; | ||
use clap::Parser; | ||
use omicron_clickhouse_admin::{Clickward, Config}; | ||
use omicron_common::cmd::fatal; | ||
use omicron_common::cmd::CmdError; | ||
use std::net::{SocketAddr, SocketAddrV6}; | ||
|
||
#[derive(Debug, Parser)] | ||
#[clap( | ||
name = "clickhouse-admin", | ||
about = "Omicron ClickHouse cluster admin server" | ||
)] | ||
enum Args { | ||
/// Start the ClickHouse admin server | ||
Run { | ||
// TODO: This address is solely for testing now. We should remove it | ||
// once we have more endpoints up and running. | ||
/// Socket address for a running clickhouse server or keeper instance | ||
#[clap(long, short = 'a', action)] | ||
clickhouse_address: SocketAddrV6, | ||
|
||
/// Address on which this server should run | ||
#[clap(long, short = 'H', action)] | ||
http_address: SocketAddrV6, | ||
|
||
/// Path to the server configuration file | ||
#[clap(long, short, action)] | ||
config: Utf8PathBuf, | ||
}, | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() { | ||
if let Err(err) = main_impl().await { | ||
fatal(err); | ||
} | ||
} | ||
|
||
async fn main_impl() -> Result<(), CmdError> { | ||
let args = Args::parse(); | ||
|
||
match args { | ||
Args::Run { clickhouse_address, http_address, config } => { | ||
let mut config = Config::from_file(&config) | ||
.map_err(|err| CmdError::Failure(anyhow!(err)))?; | ||
config.dropshot.bind_address = SocketAddr::V6(http_address); | ||
|
||
let clickward = Clickward::new(clickhouse_address); | ||
|
||
let server = | ||
omicron_clickhouse_admin::start_server(clickward, config) | ||
.await | ||
.map_err(|err| CmdError::Failure(anyhow!(err)))?; | ||
server.await.map_err(|err| { | ||
CmdError::Failure(anyhow!( | ||
"server failed after starting: {err}" | ||
)) | ||
}) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// This Source Code Form is subject to the terms of the Mozilla Public | ||
// License, v. 2.0. If a copy of the MPL was not distributed with this | ||
// file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
||
use clickhouse_admin_api::ClickhouseAddress; | ||
use dropshot::HttpError; | ||
use slog_error_chain::{InlineErrorChain, SlogInlineError}; | ||
use std::io; | ||
use std::net::SocketAddrV6; | ||
|
||
#[derive(Debug, thiserror::Error, SlogInlineError)] | ||
pub enum ClickwardError { | ||
#[error("clickward failure")] | ||
Failure { | ||
#[source] | ||
err: io::Error, | ||
}, | ||
} | ||
|
||
impl From<ClickwardError> for HttpError { | ||
fn from(err: ClickwardError) -> Self { | ||
match err { | ||
ClickwardError::Failure { .. } => { | ||
let message = InlineErrorChain::new(&err).to_string(); | ||
HttpError { | ||
status_code: http::StatusCode::INTERNAL_SERVER_ERROR, | ||
error_code: Some(String::from("Internal")), | ||
external_message: message.clone(), | ||
internal_message: message, | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct Clickward { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I realise not all tasks will interact with Clickward's libraries (some sill use the clickhouse CLIs), but I like the name and can't think of anything better :) |
||
clickhouse_address: SocketAddrV6, | ||
} | ||
|
||
impl Clickward { | ||
pub fn new(clickhouse_address: SocketAddrV6) -> Self { | ||
Self { clickhouse_address } | ||
} | ||
|
||
pub fn clickhouse_address( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We'll probably want to return the entire config struct instead, but this totally works for this PR. |
||
&self, | ||
) -> Result<ClickhouseAddress, ClickwardError> { | ||
Ok(ClickhouseAddress { clickhouse_address: self.clickhouse_address }) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// This Source Code Form is subject to the terms of the Mozilla Public | ||
// License, v. 2.0. If a copy of the MPL was not distributed with this | ||
// file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
||
use camino::Utf8Path; | ||
use camino::Utf8PathBuf; | ||
use dropshot::ConfigDropshot; | ||
use dropshot::ConfigLogging; | ||
use serde::Deserialize; | ||
use serde::Serialize; | ||
use slog_error_chain::SlogInlineError; | ||
use std::io; | ||
|
||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] | ||
pub struct Config { | ||
pub dropshot: ConfigDropshot, | ||
pub log: ConfigLogging, | ||
} | ||
impl Config { | ||
/// Load a `Config` from the given TOML file | ||
pub fn from_file(path: &Utf8Path) -> Result<Self, LoadError> { | ||
let contents = std::fs::read_to_string(path) | ||
.map_err(|err| LoadError::Read { path: path.to_owned(), err })?; | ||
toml::de::from_str(&contents) | ||
.map_err(|err| LoadError::Parse { path: path.to_owned(), err }) | ||
} | ||
} | ||
|
||
#[derive(Debug, thiserror::Error, SlogInlineError)] | ||
pub enum LoadError { | ||
#[error("failed to read {path}")] | ||
Read { | ||
path: Utf8PathBuf, | ||
#[source] | ||
err: io::Error, | ||
}, | ||
#[error("failed to parse {path} as TOML")] | ||
Parse { | ||
path: Utf8PathBuf, | ||
#[source] | ||
err: toml::de::Error, | ||
}, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// This Source Code Form is subject to the terms of the Mozilla Public | ||
// License, v. 2.0. If a copy of the MPL was not distributed with this | ||
// file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
||
use crate::Clickward; | ||
use slog::Logger; | ||
|
||
pub struct ServerContext { | ||
clickward: Clickward, | ||
_log: Logger, | ||
} | ||
|
||
impl ServerContext { | ||
pub fn new(clickward: Clickward, _log: Logger) -> Self { | ||
Self { clickward, _log } | ||
} | ||
|
||
pub fn clickward(&self) -> &Clickward { | ||
&self.clickward | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure we're actually going to need this. The config will all come from nexus such that the only thing starting up will be
clickhouse-admin
. We can't actually run the server without the rest of the config, so probably no need to pass this in.However, I'm also fine keeping it for now as it's used to test the only end point. We can remove that and the address endpoint when the time comes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's meant for testing for now. I've added a comment so it's clear that we should remove it later