Skip to content

Commit 4c0952c

Browse files
authored
[crucible-downstairs] migrate to API traits (#1768)
Migrate the downstairs admin and repair APIs to being API traits. Part of oxidecomputer/omicron#8922.
1 parent 7463797 commit 4c0952c

File tree

10 files changed

+430
-281
lines changed

10 files changed

+430
-281
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ members = [
1111
"crudd",
1212
"crutest",
1313
"downstairs",
14+
"downstairs-api",
15+
"downstairs-types",
1416
"dsc",
1517
"dsc-client",
1618
"hammer",
@@ -142,6 +144,8 @@ crucible-control-client = { path = "./control-client" }
142144
# importantly, don't use features = ["zfs_snapshot"] here, this will cause
143145
# cleanup issues in the integration tests!
144146
crucible-downstairs = { path = "./downstairs" }
147+
crucible-downstairs-api = { path = "./downstairs-api" }
148+
crucible-downstairs-types = { path = "./downstairs-types" }
145149
crucible-pantry = { path = "./pantry" }
146150
crucible-pantry-api = { path = "./pantry-api" }
147151
crucible-pantry-client = { path = "./pantry-client" }

downstairs-api/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "crucible-downstairs-api"
3+
version = "0.1.0"
4+
license = "MPL-2.0"
5+
edition = "2021"
6+
rust-version = "1.81"
7+
8+
[dependencies]
9+
crucible-common.workspace = true
10+
crucible-downstairs-types.workspace = true
11+
crucible-workspace-hack.workspace = true
12+
dropshot.workspace = true
13+
hyper.workspace = true
14+
schemars.workspace = true
15+
serde.workspace = true
16+
uuid.workspace = true

downstairs-api/src/lib.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright 2025 Oxide Computer Company
2+
3+
//! API traits for Crucible downstairs operations.
4+
5+
use crucible_common::RegionDefinition;
6+
use crucible_downstairs_types::{FileType, RunDownstairsForRegionParams};
7+
use dropshot::{
8+
Body, HttpError, HttpResponseCreated, HttpResponseOk, Path, RequestContext,
9+
TypedBody,
10+
};
11+
use hyper::Response;
12+
use schemars::JsonSchema;
13+
use serde::{Deserialize, Serialize};
14+
use uuid::Uuid;
15+
16+
/// API trait for the downstairs admin server.
17+
#[dropshot::api_description]
18+
pub trait CrucibleDownstairsAdminApi {
19+
type Context;
20+
21+
/// Start a downstairs instance for a specific region.
22+
#[endpoint {
23+
method = POST,
24+
path = "/regions/{uuid}/downstairs"
25+
}]
26+
async fn run_downstairs_for_region(
27+
rqctx: RequestContext<Self::Context>,
28+
path_param: Path<RunDownstairsForRegionPath>,
29+
run_params: TypedBody<RunDownstairsForRegionParams>,
30+
) -> Result<HttpResponseCreated<DownstairsRunningResponse>, HttpError>;
31+
}
32+
33+
/// API trait for the downstairs repair server.
34+
#[dropshot::api_description]
35+
pub trait CrucibleDownstairsRepairApi {
36+
type Context;
37+
38+
/// Get a specific extent file (data, database, or log files).
39+
#[endpoint {
40+
method = GET,
41+
path = "/newextent/{eid}/{file_type}",
42+
}]
43+
async fn get_extent_file(
44+
rqctx: RequestContext<Self::Context>,
45+
path: Path<ExtentFilePath>,
46+
) -> Result<Response<Body>, HttpError>;
47+
48+
/// Return true if the provided extent is closed or the region is read only.
49+
#[endpoint {
50+
method = GET,
51+
path = "/extent/{eid}/repair-ready",
52+
}]
53+
async fn extent_repair_ready(
54+
rqctx: RequestContext<Self::Context>,
55+
path: Path<ExtentPath>,
56+
) -> Result<HttpResponseOk<bool>, HttpError>;
57+
58+
/// Get the list of files related to an extent.
59+
///
60+
/// For a given extent, return a vec of strings representing the names of
61+
/// the files that exist for that extent.
62+
#[endpoint {
63+
method = GET,
64+
path = "/extent/{eid}/files",
65+
}]
66+
async fn get_files_for_extent(
67+
rqctx: RequestContext<Self::Context>,
68+
path: Path<ExtentPath>,
69+
) -> Result<HttpResponseOk<Vec<String>>, HttpError>;
70+
71+
/// Return the RegionDefinition describing our region.
72+
#[endpoint {
73+
method = GET,
74+
path = "/region-info",
75+
}]
76+
async fn get_region_info(
77+
rqctx: RequestContext<Self::Context>,
78+
) -> Result<HttpResponseOk<RegionDefinition>, HttpError>;
79+
80+
/// Return the region-mode describing our region.
81+
#[endpoint {
82+
method = GET,
83+
path = "/region-mode",
84+
}]
85+
async fn get_region_mode(
86+
rqctx: RequestContext<Self::Context>,
87+
) -> Result<HttpResponseOk<bool>, HttpError>;
88+
89+
/// Work queue
90+
#[endpoint {
91+
method = GET,
92+
path = "/work",
93+
}]
94+
async fn get_work(
95+
rqctx: RequestContext<Self::Context>,
96+
) -> Result<HttpResponseOk<bool>, HttpError>;
97+
}
98+
99+
// Admin API types
100+
#[derive(Deserialize, JsonSchema)]
101+
pub struct RunDownstairsForRegionPath {
102+
pub uuid: Uuid,
103+
}
104+
105+
#[derive(Serialize, JsonSchema)]
106+
pub struct DownstairsRunningResponse {
107+
pub uuid: Uuid,
108+
}
109+
110+
// Repair API types
111+
#[derive(Deserialize, JsonSchema)]
112+
pub struct ExtentPath {
113+
pub eid: u32,
114+
}
115+
116+
#[derive(Deserialize, JsonSchema)]
117+
pub struct ExtentFilePath {
118+
pub eid: u32,
119+
pub file_type: FileType,
120+
}
121+
122+
#[derive(Deserialize, JsonSchema)]
123+
pub struct JobPath {
124+
pub id: String,
125+
}

downstairs-types/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "crucible-downstairs-types"
3+
version = "0.1.0"
4+
license = "MPL-2.0"
5+
edition = "2021"
6+
rust-version = "1.81"
7+
8+
[dependencies]
9+
crucible-common.workspace = true
10+
crucible-workspace-hack.workspace = true
11+
schemars.workspace = true
12+
serde.workspace = true
13+
14+
[dev-dependencies]
15+
serde_json.workspace = true

downstairs-types/src/lib.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2025 Oxide Computer Company
2+
3+
use std::net::{IpAddr, SocketAddr};
4+
use std::path::PathBuf;
5+
6+
use schemars::JsonSchema;
7+
use serde::Deserialize;
8+
9+
#[derive(Deserialize, JsonSchema)]
10+
#[serde(rename_all = "camelCase")]
11+
pub enum FileType {
12+
#[serde(rename = "data")]
13+
Data,
14+
#[serde(rename = "db")]
15+
Database,
16+
#[serde(rename = "db_shm")]
17+
DatabaseSharedMemory,
18+
#[serde(rename = "db_wal")]
19+
DatabaseLog,
20+
}
21+
22+
#[derive(Deserialize, JsonSchema)]
23+
pub struct FileSpec {
24+
pub eid: u32,
25+
pub file_type: FileType,
26+
}
27+
28+
// Admin API types
29+
#[derive(Deserialize, JsonSchema)]
30+
pub struct RunDownstairsForRegionParams {
31+
pub address: IpAddr,
32+
pub data: PathBuf,
33+
pub oximeter: Option<SocketAddr>,
34+
pub lossy: bool,
35+
pub port: u16,
36+
pub rport: u16,
37+
pub read_errors: bool,
38+
pub write_errors: bool,
39+
pub flush_errors: bool,
40+
pub cert_pem: Option<String>,
41+
pub key_pem: Option<String>,
42+
pub root_cert_pem: Option<String>,
43+
pub read_only: bool,
44+
}

downstairs/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@ rust-version = "1.81"
88

99
[dependencies]
1010
anyhow.workspace = true
11+
async-trait.workspace = true
1112
bincode.workspace = true
1213
bytes.workspace = true
1314
chrono.workspace = true
1415
clap.workspace = true
1516
crucible-common.workspace = true
17+
crucible-downstairs-api.workspace = true
18+
crucible-downstairs-types.workspace = true
1619
crucible-protocol.workspace = true
1720
dropshot.workspace = true
1821
futures-core.workspace = true

0 commit comments

Comments
 (0)