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

[reconfigurator] clickhouse-admin SMF service with dropshot server #6304

Merged
merged 10 commits into from
Aug 14, 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
48 changes: 48 additions & 0 deletions Cargo.lock

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

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ members = [
"api_identity",
"bootstore",
"certificates",
"clickhouse-admin",
"clickhouse-admin/api",
"clients/bootstrap-agent-client",
"clients/cockroach-admin-client",
"clients/ddm-admin-client",
Expand Down Expand Up @@ -111,6 +113,8 @@ default-members = [
"api_identity",
"bootstore",
"certificates",
"clickhouse-admin",
"clickhouse-admin/api",
"clients/bootstrap-agent-client",
"clients/cockroach-admin-client",
"clients/ddm-admin-client",
Expand Down Expand Up @@ -292,6 +296,7 @@ cfg-if = "1.0"
chrono = { version = "0.4", features = [ "serde" ] }
ciborium = "0.2.2"
clap = { version = "4.5", features = ["cargo", "derive", "env", "wrap_help"] }
clickhouse-admin-api = { path = "clickhouse-admin/api" }
clickward = { git = "https://github.com/oxidecomputer/clickward", rev = "ceec762e6a87d2a22bf56792a3025e145caa095e" }
cockroach-admin-api = { path = "cockroach-admin/api" }
cockroach-admin-client = { path = "clients/cockroach-admin-client" }
Expand Down Expand Up @@ -414,6 +419,7 @@ nexus-test-utils = { path = "nexus/test-utils" }
nexus-types = { path = "nexus/types" }
num-integer = "0.1.46"
num = { version = "0.4.3", default-features = false, features = [ "libm" ] }
omicron-clickhouse-admin = { path = "clickhouse-admin" }
omicron-certificates = { path = "certificates" }
omicron-cockroach-admin = { path = "cockroach-admin" }
omicron-common = { path = "common" }
Expand Down
42 changes: 42 additions & 0 deletions clickhouse-admin/Cargo.toml
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
16 changes: 16 additions & 0 deletions clickhouse-admin/api/Cargo.toml
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
28 changes: 28 additions & 0 deletions clickhouse-admin/api/src/lib.rs
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,
}
68 changes: 68 additions & 0 deletions clickhouse-admin/src/bin/clickhouse-admin.rs
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,
Copy link
Contributor

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.

Copy link
Contributor Author

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


/// 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}"
))
})
}
}
}
51 changes: 51 additions & 0 deletions clickhouse-admin/src/clickward.rs
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 {
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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(
Copy link
Contributor

Choose a reason for hiding this comment

The 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 })
}
}
43 changes: 43 additions & 0 deletions clickhouse-admin/src/config.rs
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,
},
}
21 changes: 21 additions & 0 deletions clickhouse-admin/src/context.rs
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
}
}
Loading
Loading