Skip to content

Commit b034821

Browse files
authored
Add omdb support for clickhouse policy (#6892)
Uses new internal nexus API endpoints.
1 parent 778a7e9 commit b034821

File tree

9 files changed

+388
-7
lines changed

9 files changed

+388
-7
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

clients/nexus-client/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ progenitor::generate_api!(
3030
// (e.g., diff'ing) that's implemented on our local type.
3131
Blueprint = nexus_types::deployment::Blueprint,
3232
Certificate = omicron_common::api::internal::nexus::Certificate,
33+
ClickhouseMode = nexus_types::deployment::ClickhouseMode,
34+
ClickhousePolicy = nexus_types::deployment::ClickhousePolicy,
3335
DatasetKind = omicron_common::api::internal::shared::DatasetKind,
3436
DnsConfigParams = nexus_types::internal_api::params::DnsConfigParams,
3537
DnsConfigZone = nexus_types::internal_api::params::DnsConfigZone,

dev-tools/omdb/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ futures.workspace = true
2626
gateway-client.workspace = true
2727
gateway-messages.workspace = true
2828
gateway-test-utils.workspace = true
29+
http.workspace = true
2930
humantime.workspace = true
3031
internal-dns-resolver.workspace = true
3132
internal-dns-types.workspace = true
@@ -34,6 +35,7 @@ nexus-client.workspace = true
3435
nexus-config.workspace = true
3536
nexus-db-model.workspace = true
3637
nexus-db-queries.workspace = true
38+
nexus-inventory.workspace = true
3739
nexus-reconfigurator-preparation.workspace = true
3840
nexus-saga-recovery.workspace = true
3941
nexus-types.workspace = true

dev-tools/omdb/src/bin/omdb/nexus.rs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use clap::Subcommand;
2222
use clap::ValueEnum;
2323
use futures::future::try_join;
2424
use futures::TryStreamExt;
25+
use http::StatusCode;
2526
use internal_dns_types::names::ServiceName;
2627
use itertools::Itertools;
2728
use nexus_client::types::ActivationReason;
@@ -34,8 +35,11 @@ use nexus_client::types::SagaState;
3435
use nexus_client::types::SledSelector;
3536
use nexus_client::types::UninitializedSledId;
3637
use nexus_db_queries::db::lookup::LookupPath;
38+
use nexus_inventory::now_db_precision;
3739
use nexus_saga_recovery::LastPass;
3840
use nexus_types::deployment::Blueprint;
41+
use nexus_types::deployment::ClickhouseMode;
42+
use nexus_types::deployment::ClickhousePolicy;
3943
use nexus_types::internal_api::background::AbandonedVmmReaperStatus;
4044
use nexus_types::internal_api::background::InstanceReincarnationStatus;
4145
use nexus_types::internal_api::background::InstanceUpdaterStatus;
@@ -101,6 +105,8 @@ enum NexusCommands {
101105
BackgroundTasks(BackgroundTasksArgs),
102106
/// interact with blueprints
103107
Blueprints(BlueprintsArgs),
108+
/// Interact with clickhouse policy
109+
ClickhousePolicy(ClickhousePolicyArgs),
104110
/// view sagas, create and complete demo sagas
105111
Sagas(SagasArgs),
106112
/// interact with sleds
@@ -299,6 +305,42 @@ struct BlueprintImportArgs {
299305
input: Utf8PathBuf,
300306
}
301307

308+
#[derive(Debug, Args)]
309+
struct ClickhousePolicyArgs {
310+
#[command(subcommand)]
311+
command: ClickhousePolicyCommands,
312+
}
313+
314+
#[derive(Debug, Subcommand)]
315+
enum ClickhousePolicyCommands {
316+
/// Get the current policy
317+
Get,
318+
/// Set the new policy
319+
Set(ClickhousePolicySetArgs),
320+
}
321+
322+
#[derive(Debug, Args)]
323+
struct ClickhousePolicySetArgs {
324+
mode: ClickhousePolicyMode,
325+
326+
/// The number of servers in a clickhouse cluster
327+
#[arg(long, default_value_t = 3)]
328+
target_servers: u8,
329+
/// The number of keepers in a clickhouse cluster
330+
#[arg(long, default_value_t = 5)]
331+
target_keepers: u8,
332+
}
333+
334+
#[derive(Debug, Clone, Copy, ValueEnum)]
335+
enum ClickhousePolicyMode {
336+
/// Run only a single node clickhouse instance
337+
SingleNodeOnly,
338+
// Run only a clickhouse cluster
339+
ClusterOnly,
340+
// Run both single-node and clustered clickhouse deployments
341+
Both,
342+
}
343+
302344
#[derive(Debug, Args)]
303345
struct SagasArgs {
304346
#[command(subcommand)]
@@ -494,6 +536,18 @@ impl NexusArgs {
494536
cmd_nexus_blueprints_import(&client, token, args).await
495537
}
496538

539+
NexusCommands::ClickhousePolicy(ClickhousePolicyArgs {
540+
command,
541+
}) => match command {
542+
ClickhousePolicyCommands::Get => {
543+
cmd_nexus_clickhouse_policy_get(&client).await
544+
}
545+
ClickhousePolicyCommands::Set(args) => {
546+
let token = omdb.check_allow_destructive()?;
547+
cmd_nexus_clickhouse_policy_set(&client, args, token).await
548+
}
549+
},
550+
497551
NexusCommands::Sagas(SagasArgs { command }) => {
498552
if self.nexus_internal_url.is_none() {
499553
eprintln!(
@@ -2376,6 +2430,100 @@ async fn cmd_nexus_blueprints_import(
23762430
Ok(())
23772431
}
23782432

2433+
async fn cmd_nexus_clickhouse_policy_get(
2434+
client: &nexus_client::Client,
2435+
) -> Result<(), anyhow::Error> {
2436+
let res = client.clickhouse_policy_get().await;
2437+
2438+
match res {
2439+
Err(err) => {
2440+
if err.status() == Some(StatusCode::NOT_FOUND) {
2441+
println!(
2442+
"No clickhouse policy: \
2443+
Defaulting to single-node deployment"
2444+
);
2445+
} else {
2446+
eprintln!("error: {:#}", err);
2447+
}
2448+
}
2449+
Ok(policy) => {
2450+
println!("Clickhouse Policy: ");
2451+
println!(" version: {}", policy.version);
2452+
println!(" creation time: {}", policy.time_created);
2453+
match policy.mode {
2454+
ClickhouseMode::SingleNodeOnly => {
2455+
println!(" mode: single-node-only");
2456+
}
2457+
ClickhouseMode::ClusterOnly {
2458+
target_servers,
2459+
target_keepers,
2460+
} => {
2461+
println!(" mode: cluster-only");
2462+
println!(" target-servers: {}", target_servers);
2463+
println!(" target-keepers: {}", target_keepers);
2464+
}
2465+
ClickhouseMode::Both { target_servers, target_keepers } => {
2466+
println!(" mode: both single-node and cluster");
2467+
println!(" target-servers: {}", target_servers);
2468+
println!(" target-keepers: {}", target_keepers);
2469+
}
2470+
}
2471+
}
2472+
}
2473+
2474+
Ok(())
2475+
}
2476+
2477+
async fn cmd_nexus_clickhouse_policy_set(
2478+
client: &nexus_client::Client,
2479+
args: &ClickhousePolicySetArgs,
2480+
_destruction_token: DestructiveOperationToken,
2481+
) -> Result<(), anyhow::Error> {
2482+
let mode = match args.mode {
2483+
ClickhousePolicyMode::SingleNodeOnly => ClickhouseMode::SingleNodeOnly,
2484+
ClickhousePolicyMode::ClusterOnly => ClickhouseMode::ClusterOnly {
2485+
target_servers: args.target_servers,
2486+
target_keepers: args.target_keepers,
2487+
},
2488+
ClickhousePolicyMode::Both => ClickhouseMode::Both {
2489+
target_servers: args.target_servers,
2490+
target_keepers: args.target_keepers,
2491+
},
2492+
};
2493+
2494+
let res = client.clickhouse_policy_get().await;
2495+
let new_policy = match res {
2496+
Err(err) => {
2497+
if err.status() == Some(StatusCode::NOT_FOUND) {
2498+
ClickhousePolicy {
2499+
version: 1,
2500+
mode,
2501+
time_created: now_db_precision(),
2502+
}
2503+
} else {
2504+
eprintln!("error: {:#}", err);
2505+
return Err(err).context("retrieving clickhouse policy");
2506+
}
2507+
}
2508+
Ok(policy) => ClickhousePolicy {
2509+
version: policy.version + 1,
2510+
mode,
2511+
time_created: now_db_precision(),
2512+
},
2513+
};
2514+
2515+
client.clickhouse_policy_set(&new_policy).await.with_context(|| {
2516+
format!("inserting new context at version {}", new_policy.version)
2517+
})?;
2518+
2519+
println!(
2520+
"Successfully inserted new policy at version {}",
2521+
new_policy.version
2522+
);
2523+
2524+
Ok(())
2525+
}
2526+
23792527
/// Runs `omdb nexus sagas list`
23802528
async fn cmd_nexus_sagas_list(
23812529
client: &nexus_client::Client,

dev-tools/omdb/tests/usage_errors.out

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -467,11 +467,12 @@ Debug a specific Nexus instance
467467
Usage: omdb nexus [OPTIONS] <COMMAND>
468468

469469
Commands:
470-
background-tasks print information about background tasks
471-
blueprints interact with blueprints
472-
sagas view sagas, create and complete demo sagas
473-
sleds interact with sleds
474-
help Print this message or the help of the given subcommand(s)
470+
background-tasks print information about background tasks
471+
blueprints interact with blueprints
472+
clickhouse-policy Interact with clickhouse policy
473+
sagas view sagas, create and complete demo sagas
474+
sleds interact with sleds
475+
help Print this message or the help of the given subcommand(s)
475476

476477
Options:
477478
--log-level <LOG_LEVEL> log level filter [env: LOG_LEVEL=] [default: warn]

nexus/internal-api/src/lib.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use dropshot::{
1212
use nexus_types::{
1313
deployment::{
1414
Blueprint, BlueprintMetadata, BlueprintTarget, BlueprintTargetSet,
15+
ClickhousePolicy,
1516
},
1617
external_api::{
1718
params::{PhysicalDiskPath, SledSelector, UninitializedSledId},
@@ -530,6 +531,25 @@ pub trait NexusInternalApi {
530531
path_params: Path<ProbePathParam>,
531532
query_params: Query<PaginatedById>,
532533
) -> Result<HttpResponseOk<Vec<ProbeInfo>>, HttpError>;
534+
535+
/// Get the current clickhouse policy
536+
#[endpoint {
537+
method = GET,
538+
path = "/clickhouse/policy"
539+
}]
540+
async fn clickhouse_policy_get(
541+
rqctx: RequestContext<Self::Context>,
542+
) -> Result<HttpResponseOk<ClickhousePolicy>, HttpError>;
543+
544+
/// Set the new clickhouse policy
545+
#[endpoint {
546+
method = POST,
547+
path = "/clickhouse/policy"
548+
}]
549+
async fn clickhouse_policy_set(
550+
rqctx: RequestContext<Self::Context>,
551+
policy: TypedBody<ClickhousePolicy>,
552+
) -> Result<HttpResponseUpdatedNoContent, HttpError>;
533553
}
534554

535555
/// Path parameters for Sled Agent requests (internal API)

nexus/src/internal_api/http_entrypoints.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use nexus_types::deployment::Blueprint;
2424
use nexus_types::deployment::BlueprintMetadata;
2525
use nexus_types::deployment::BlueprintTarget;
2626
use nexus_types::deployment::BlueprintTargetSet;
27+
use nexus_types::deployment::ClickhousePolicy;
2728
use nexus_types::external_api::params::PhysicalDiskPath;
2829
use nexus_types::external_api::params::SledSelector;
2930
use nexus_types::external_api::params::UninitializedSledId;
@@ -939,4 +940,51 @@ impl NexusInternalApi for NexusInternalApiImpl {
939940
.instrument_dropshot_handler(&rqctx, handler)
940941
.await
941942
}
943+
944+
async fn clickhouse_policy_get(
945+
rqctx: RequestContext<Self::Context>,
946+
) -> Result<HttpResponseOk<ClickhousePolicy>, HttpError> {
947+
let apictx = &rqctx.context().context;
948+
let handler = async {
949+
let nexus = &apictx.nexus;
950+
let opctx =
951+
crate::context::op_context_for_internal_api(&rqctx).await;
952+
match nexus.datastore().clickhouse_policy_get_latest(&opctx).await?
953+
{
954+
Some(policy) => Ok(HttpResponseOk(policy)),
955+
None => Err(HttpError::for_not_found(
956+
None,
957+
"No clickhouse policy in database".into(),
958+
)),
959+
}
960+
};
961+
apictx
962+
.internal_latencies
963+
.instrument_dropshot_handler(&rqctx, handler)
964+
.await
965+
}
966+
967+
async fn clickhouse_policy_set(
968+
rqctx: RequestContext<Self::Context>,
969+
policy: TypedBody<ClickhousePolicy>,
970+
) -> Result<HttpResponseUpdatedNoContent, HttpError> {
971+
let apictx = &rqctx.context().context;
972+
let nexus = &apictx.nexus;
973+
let handler = async {
974+
let opctx =
975+
crate::context::op_context_for_internal_api(&rqctx).await;
976+
nexus
977+
.datastore()
978+
.clickhouse_policy_insert_latest_version(
979+
&opctx,
980+
&policy.into_inner(),
981+
)
982+
.await?;
983+
Ok(HttpResponseUpdatedNoContent())
984+
};
985+
apictx
986+
.internal_latencies
987+
.instrument_dropshot_handler(&rqctx, handler)
988+
.await
989+
}
942990
}

nexus/types/src/deployment/planning_input.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -887,15 +887,16 @@ pub struct Policy {
887887
pub clickhouse_policy: Option<ClickhousePolicy>,
888888
}
889889

890-
#[derive(Debug, Clone, Serialize, Deserialize)]
890+
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
891891
pub struct ClickhousePolicy {
892892
pub version: u32,
893893
pub mode: ClickhouseMode,
894894
pub time_created: DateTime<Utc>,
895895
}
896896

897897
/// How to deploy clickhouse nodes
898-
#[derive(Debug, Clone, Serialize, Deserialize)]
898+
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
899+
#[serde(rename_all = "snake_case", tag = "type", content = "value")]
899900
pub enum ClickhouseMode {
900901
SingleNodeOnly,
901902
ClusterOnly { target_servers: u8, target_keepers: u8 },

0 commit comments

Comments
 (0)