From 82af9731ff897765f757d37a6e9e2daa9b88f13e Mon Sep 17 00:00:00 2001 From: Rain Date: Tue, 3 Mar 2026 17:05:37 -0800 Subject: [PATCH] [spr] initial version Created using spr 1.3.6-beta.1 --- Cargo.lock | 14 +- Cargo.toml | 2 + pantry-api/Cargo.toml | 5 +- pantry-api/src/lib.rs | 162 ++++---------------- pantry-types/Cargo.toml | 3 +- pantry-types/src/lib.rs | 33 +--- pantry-types/src/pantry.rs | 3 + pantry-types/versions/Cargo.toml | 11 ++ pantry-types/versions/src/impls/mod.rs | 6 + pantry-types/versions/src/initial/mod.rs | 5 + pantry-types/versions/src/initial/pantry.rs | 126 +++++++++++++++ pantry-types/versions/src/latest.rs | 28 ++++ pantry-types/versions/src/lib.rs | 33 ++++ pantry/src/pantry.rs | 4 +- pantry/src/server.rs | 13 +- 15 files changed, 274 insertions(+), 174 deletions(-) create mode 100644 pantry-types/src/pantry.rs create mode 100644 pantry-types/versions/Cargo.toml create mode 100644 pantry-types/versions/src/impls/mod.rs create mode 100644 pantry-types/versions/src/initial/mod.rs create mode 100644 pantry-types/versions/src/initial/pantry.rs create mode 100644 pantry-types/versions/src/latest.rs create mode 100644 pantry-types/versions/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 55b46d998..cd42693ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1487,13 +1487,10 @@ dependencies = [ name = "crucible-pantry-api" version = "0.1.0" dependencies = [ - "crucible-client-types", - "crucible-pantry-types", + "crucible-pantry-types-versions", "crucible-workspace-hack", "dropshot", "dropshot-api-manager-types", - "schemars 0.8.22", - "serde", ] [[package]] @@ -1516,6 +1513,15 @@ dependencies = [ name = "crucible-pantry-types" version = "0.1.0" dependencies = [ + "crucible-pantry-types-versions", + "crucible-workspace-hack", +] + +[[package]] +name = "crucible-pantry-types-versions" +version = "0.1.0" +dependencies = [ + "crucible-client-types", "crucible-workspace-hack", "schemars 0.8.22", "serde", diff --git a/Cargo.toml b/Cargo.toml index e058cc5e6..466da43d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ members = [ "pantry-api", "pantry-client", "pantry-types", + "pantry-types/versions", "protocol", "repair-client", "smf", @@ -157,6 +158,7 @@ crucible-pantry = { path = "./pantry" } crucible-pantry-api = { path = "./pantry-api" } crucible-pantry-client = { path = "./pantry-client" } crucible-pantry-types = { path = "./pantry-types" } +crucible-pantry-types-versions = { path = "./pantry-types/versions" } crucible-protocol = { path = "./protocol" } crucible-raw-extent = { path = "./raw-extent" } crucible-verify-raw = { path = "./verify-raw" } diff --git a/pantry-api/Cargo.toml b/pantry-api/Cargo.toml index 8a8da76d2..125bd243c 100644 --- a/pantry-api/Cargo.toml +++ b/pantry-api/Cargo.toml @@ -5,10 +5,7 @@ edition = "2024" license = "MPL-2.0" [dependencies] -crucible-client-types.workspace = true -crucible-pantry-types.workspace = true +crucible-pantry-types-versions.workspace = true crucible-workspace-hack.workspace = true dropshot.workspace = true dropshot-api-manager-types.workspace = true -schemars.workspace = true -serde.workspace = true diff --git a/pantry-api/src/lib.rs b/pantry-api/src/lib.rs index f9f207089..dd7ca4829 100644 --- a/pantry-api/src/lib.rs +++ b/pantry-api/src/lib.rs @@ -1,14 +1,11 @@ -// Copyright 2025 Oxide Computer Company +// Copyright 2026 Oxide Computer Company -use crucible_client_types::{ReplaceResult, VolumeConstructionRequest}; -use crucible_pantry_types::*; +use crucible_pantry_types_versions::latest; use dropshot::{ HttpError, HttpResponseDeleted, HttpResponseOk, HttpResponseUpdatedNoContent, Path, RequestContext, TypedBody, }; use dropshot_api_manager_types::api_versions; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; api_versions!([ // WHEN CHANGING THE API (part 1 of 2): @@ -48,7 +45,7 @@ pub trait CruciblePantryApi { }] async fn pantry_status( rqctx: RequestContext, - ) -> Result, HttpError>; + ) -> Result, HttpError>; /// Get a current Volume's status #[endpoint { @@ -57,8 +54,8 @@ pub trait CruciblePantryApi { }] async fn volume_status( rqctx: RequestContext, - path: Path, - ) -> Result, HttpError>; + path: Path, + ) -> Result, HttpError>; /// Construct a volume from a VolumeConstructionRequest, storing the result in /// the Pantry. @@ -68,9 +65,9 @@ pub trait CruciblePantryApi { }] async fn attach( rqctx: RequestContext, - path: Path, - body: TypedBody, - ) -> Result, HttpError>; + path: Path, + body: TypedBody, + ) -> Result, HttpError>; /// Construct a volume from a VolumeConstructionRequest, storing the result in /// the Pantry. Activate in a separate job so as not to block the request. @@ -80,8 +77,8 @@ pub trait CruciblePantryApi { }] async fn attach_activate_background( rqctx: RequestContext, - path: Path, - body: TypedBody, + path: Path, + body: TypedBody, ) -> Result; /// Call a volume's target_replace function @@ -91,9 +88,9 @@ pub trait CruciblePantryApi { }] async fn replace( rqctx: RequestContext, - path: Path, - body: TypedBody, - ) -> Result, HttpError>; + path: Path, + body: TypedBody, + ) -> Result, HttpError>; /// Poll to see if a Pantry background job is done #[endpoint { @@ -102,8 +99,8 @@ pub trait CruciblePantryApi { }] async fn is_job_finished( rqctx: RequestContext, - path: Path, - ) -> Result, HttpError>; + path: Path, + ) -> Result, HttpError>; /// Block on returning a Pantry background job result, then return 200 OK if the /// job executed OK, 500 otherwise. @@ -113,8 +110,8 @@ pub trait CruciblePantryApi { }] async fn job_result_ok( rqctx: RequestContext, - path: Path, - ) -> Result, HttpError>; + path: Path, + ) -> Result, HttpError>; /// Import data from a URL into a volume #[endpoint { @@ -123,9 +120,9 @@ pub trait CruciblePantryApi { }] async fn import_from_url( rqctx: RequestContext, - path: Path, - body: TypedBody, - ) -> Result, HttpError>; + path: Path, + body: TypedBody, + ) -> Result, HttpError>; /// Take a snapshot of a volume #[endpoint { @@ -134,8 +131,8 @@ pub trait CruciblePantryApi { }] async fn snapshot( rqctx: RequestContext, - path: Path, - body: TypedBody, + path: Path, + body: TypedBody, ) -> Result; /// Bulk write data into a volume at a specified offset @@ -145,8 +142,8 @@ pub trait CruciblePantryApi { }] async fn bulk_write( rqctx: RequestContext, - path: Path, - body: TypedBody, + path: Path, + body: TypedBody, ) -> Result; /// Bulk read data from a volume at a specified offset @@ -156,9 +153,9 @@ pub trait CruciblePantryApi { }] async fn bulk_read( rqctx: RequestContext, - path: Path, - body: TypedBody, - ) -> Result, HttpError>; + path: Path, + body: TypedBody, + ) -> Result, HttpError>; /// Scrub the volume (copy blocks from read-only parent to subvolumes) #[endpoint { @@ -167,8 +164,8 @@ pub trait CruciblePantryApi { }] async fn scrub( rqctx: RequestContext, - path: Path, - ) -> Result, HttpError>; + path: Path, + ) -> Result, HttpError>; /// Validate the digest of a whole volume #[endpoint { @@ -177,9 +174,9 @@ pub trait CruciblePantryApi { }] async fn validate( rqctx: RequestContext, - path: Path, - body: TypedBody, - ) -> Result, HttpError>; + path: Path, + body: TypedBody, + ) -> Result, HttpError>; /// Deactivate a volume, removing it from the Pantry #[endpoint { @@ -188,99 +185,6 @@ pub trait CruciblePantryApi { }] async fn detach( rqctx: RequestContext, - path: Path, + path: Path, ) -> Result; } - -#[derive(Deserialize, JsonSchema)] -pub struct VolumePath { - pub id: String, -} - -#[derive(Deserialize, JsonSchema)] -pub struct AttachRequest { - pub volume_construction_request: VolumeConstructionRequest, -} - -#[derive(Serialize, JsonSchema)] -pub struct AttachResult { - pub id: String, -} - -#[derive(Deserialize, JsonSchema)] -pub struct AttachBackgroundRequest { - pub volume_construction_request: VolumeConstructionRequest, - pub job_id: String, -} - -#[derive(Deserialize, JsonSchema)] -pub struct ReplaceRequest { - pub volume_construction_request: VolumeConstructionRequest, -} - -#[derive(Deserialize, JsonSchema)] -pub struct JobPath { - pub id: String, -} - -#[derive(Serialize, JsonSchema)] -pub struct JobPollResponse { - pub job_is_finished: bool, -} - -#[derive(Serialize, JsonSchema)] -pub struct JobResultOkResponse { - pub job_result_ok: bool, -} - -#[derive(Deserialize, JsonSchema)] -pub struct ImportFromUrlRequest { - pub url: String, - pub expected_digest: Option, -} - -#[derive(Serialize, JsonSchema)] -pub struct ImportFromUrlResponse { - pub job_id: String, -} - -#[derive(Deserialize, JsonSchema)] -pub struct SnapshotRequest { - pub snapshot_id: String, -} - -#[derive(Deserialize, JsonSchema)] -pub struct BulkWriteRequest { - pub offset: u64, - pub base64_encoded_data: String, -} - -#[derive(Deserialize, JsonSchema)] -pub struct BulkReadRequest { - pub offset: u64, - pub size: usize, -} - -#[derive(Serialize, JsonSchema)] -pub struct BulkReadResponse { - pub base64_encoded_data: String, -} - -#[derive(Serialize, JsonSchema)] -pub struct ScrubResponse { - pub job_id: String, -} - -#[derive(Deserialize, JsonSchema)] -pub struct ValidateRequest { - pub expected_digest: ExpectedDigest, - - // Size to validate in bytes, starting from offset 0. If not specified, the - // total volume size is used. - pub size_to_validate: Option, -} - -#[derive(Serialize, JsonSchema)] -pub struct ValidateResponse { - pub job_id: String, -} diff --git a/pantry-types/Cargo.toml b/pantry-types/Cargo.toml index 66995a1a4..a515485d1 100644 --- a/pantry-types/Cargo.toml +++ b/pantry-types/Cargo.toml @@ -5,6 +5,5 @@ edition = "2024" license = "MPL-2.0" [dependencies] +crucible-pantry-types-versions.workspace = true crucible-workspace-hack.workspace = true -schemars.workspace = true -serde.workspace = true diff --git a/pantry-types/src/lib.rs b/pantry-types/src/lib.rs index 6254fb277..d9a78286f 100644 --- a/pantry-types/src/lib.rs +++ b/pantry-types/src/lib.rs @@ -1,32 +1,3 @@ -// Copyright 2025 Oxide Computer Company +// Copyright 2026 Oxide Computer Company -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, JsonSchema)] -pub struct PantryStatus { - /// Which volumes does this Pantry know about? Note this may include volumes - /// that are no longer active, and haven't been garbage collected yet. - pub volumes: Vec, - - /// How many job handles? - pub num_job_handles: usize, -} - -#[derive(Serialize, JsonSchema)] -pub struct VolumeStatus { - /// Is the Volume currently active? - pub active: bool, - - /// Has the Pantry ever seen this Volume active? - pub seen_active: bool, - - /// How many job handles are there for this Volume? - pub num_job_handles: usize, -} - -#[derive(Debug, Deserialize, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum ExpectedDigest { - Sha256(String), -} +pub mod pantry; diff --git a/pantry-types/src/pantry.rs b/pantry-types/src/pantry.rs new file mode 100644 index 000000000..8a6c4d053 --- /dev/null +++ b/pantry-types/src/pantry.rs @@ -0,0 +1,3 @@ +// Copyright 2026 Oxide Computer Company + +pub use crucible_pantry_types_versions::latest::pantry::*; diff --git a/pantry-types/versions/Cargo.toml b/pantry-types/versions/Cargo.toml new file mode 100644 index 000000000..c55dfad50 --- /dev/null +++ b/pantry-types/versions/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "crucible-pantry-types-versions" +version = "0.1.0" +license = "MPL-2.0" +edition = "2024" + +[dependencies] +crucible-client-types.workspace = true +crucible-workspace-hack.workspace = true +schemars.workspace = true +serde.workspace = true diff --git a/pantry-types/versions/src/impls/mod.rs b/pantry-types/versions/src/impls/mod.rs new file mode 100644 index 000000000..70e2f0ff1 --- /dev/null +++ b/pantry-types/versions/src/impls/mod.rs @@ -0,0 +1,6 @@ +// Copyright 2026 Oxide Computer Company + +//! Functional code for the latest versions of types. + +// Currently a placeholder. Remove this comment when functional code is added in +// a submodule. diff --git a/pantry-types/versions/src/initial/mod.rs b/pantry-types/versions/src/initial/mod.rs new file mode 100644 index 000000000..2db1d31db --- /dev/null +++ b/pantry-types/versions/src/initial/mod.rs @@ -0,0 +1,5 @@ +// Copyright 2026 Oxide Computer Company + +//! Version `INITIAL` of the Crucible Pantry API. + +pub mod pantry; diff --git a/pantry-types/versions/src/initial/pantry.rs b/pantry-types/versions/src/initial/pantry.rs new file mode 100644 index 000000000..8c1173cee --- /dev/null +++ b/pantry-types/versions/src/initial/pantry.rs @@ -0,0 +1,126 @@ +// Copyright 2026 Oxide Computer Company + +use crucible_client_types::VolumeConstructionRequest; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, JsonSchema)] +pub struct PantryStatus { + /// Which volumes does this Pantry know about? Note this may include volumes + /// that are no longer active, and haven't been garbage collected yet. + pub volumes: Vec, + + /// How many job handles? + pub num_job_handles: usize, +} + +#[derive(Serialize, JsonSchema)] +pub struct VolumeStatus { + /// Is the Volume currently active? + pub active: bool, + + /// Has the Pantry ever seen this Volume active? + pub seen_active: bool, + + /// How many job handles are there for this Volume? + pub num_job_handles: usize, +} + +#[derive(Debug, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum ExpectedDigest { + Sha256(String), +} + +#[derive(Deserialize, JsonSchema)] +pub struct VolumePath { + pub id: String, +} + +#[derive(Deserialize, JsonSchema)] +pub struct AttachRequest { + pub volume_construction_request: VolumeConstructionRequest, +} + +#[derive(Serialize, JsonSchema)] +pub struct AttachResult { + pub id: String, +} + +#[derive(Deserialize, JsonSchema)] +pub struct AttachBackgroundRequest { + pub volume_construction_request: VolumeConstructionRequest, + pub job_id: String, +} + +#[derive(Deserialize, JsonSchema)] +pub struct ReplaceRequest { + pub volume_construction_request: VolumeConstructionRequest, +} + +#[derive(Deserialize, JsonSchema)] +pub struct JobPath { + pub id: String, +} + +#[derive(Serialize, JsonSchema)] +pub struct JobPollResponse { + pub job_is_finished: bool, +} + +#[derive(Serialize, JsonSchema)] +pub struct JobResultOkResponse { + pub job_result_ok: bool, +} + +#[derive(Deserialize, JsonSchema)] +pub struct ImportFromUrlRequest { + pub url: String, + pub expected_digest: Option, +} + +#[derive(Serialize, JsonSchema)] +pub struct ImportFromUrlResponse { + pub job_id: String, +} + +#[derive(Deserialize, JsonSchema)] +pub struct SnapshotRequest { + pub snapshot_id: String, +} + +#[derive(Deserialize, JsonSchema)] +pub struct BulkWriteRequest { + pub offset: u64, + pub base64_encoded_data: String, +} + +#[derive(Deserialize, JsonSchema)] +pub struct BulkReadRequest { + pub offset: u64, + pub size: usize, +} + +#[derive(Serialize, JsonSchema)] +pub struct BulkReadResponse { + pub base64_encoded_data: String, +} + +#[derive(Serialize, JsonSchema)] +pub struct ScrubResponse { + pub job_id: String, +} + +#[derive(Deserialize, JsonSchema)] +pub struct ValidateRequest { + pub expected_digest: ExpectedDigest, + + // Size to validate in bytes, starting from offset 0. If not specified, the + // total volume size is used. + pub size_to_validate: Option, +} + +#[derive(Serialize, JsonSchema)] +pub struct ValidateResponse { + pub job_id: String, +} diff --git a/pantry-types/versions/src/latest.rs b/pantry-types/versions/src/latest.rs new file mode 100644 index 000000000..9ff47b189 --- /dev/null +++ b/pantry-types/versions/src/latest.rs @@ -0,0 +1,28 @@ +// Copyright 2026 Oxide Computer Company + +pub mod pantry { + pub use crate::v1::pantry::AttachBackgroundRequest; + pub use crate::v1::pantry::AttachRequest; + pub use crate::v1::pantry::AttachResult; + pub use crate::v1::pantry::BulkReadRequest; + pub use crate::v1::pantry::BulkReadResponse; + pub use crate::v1::pantry::BulkWriteRequest; + pub use crate::v1::pantry::ExpectedDigest; + pub use crate::v1::pantry::ImportFromUrlRequest; + pub use crate::v1::pantry::ImportFromUrlResponse; + pub use crate::v1::pantry::JobPath; + pub use crate::v1::pantry::JobPollResponse; + pub use crate::v1::pantry::JobResultOkResponse; + pub use crate::v1::pantry::PantryStatus; + pub use crate::v1::pantry::ReplaceRequest; + pub use crate::v1::pantry::ScrubResponse; + pub use crate::v1::pantry::SnapshotRequest; + pub use crate::v1::pantry::ValidateRequest; + pub use crate::v1::pantry::ValidateResponse; + pub use crate::v1::pantry::VolumePath; + pub use crate::v1::pantry::VolumeStatus; + + // Re-export ReplaceResult from crucible_client_types since the pantry API + // uses it. + pub use crucible_client_types::ReplaceResult; +} diff --git a/pantry-types/versions/src/lib.rs b/pantry-types/versions/src/lib.rs new file mode 100644 index 000000000..ddc79390d --- /dev/null +++ b/pantry-types/versions/src/lib.rs @@ -0,0 +1,33 @@ +// Copyright 2026 Oxide Computer Company + +//! Versioned types for the Crucible Pantry API. +//! +//! # Adding a new API version +//! +//! When adding a new API version N with added or changed types: +//! +//! 1. Create /mod.rs, where is the lowercase +//! form of the new version's identifier, as defined in the API trait's +//! `api_versions!` macro. +//! +//! 2. Add to the end of this list: +//! +//! ```rust,ignore +//! #[path = "/mod.rs"] +//! pub mod vN; +//! ``` +//! +//! 3. Add your types to the new module, mirroring the module structure from +//! earlier versions. +//! +//! 4. Update `latest.rs` with new and updated types from the new version. +//! +//! For more information, see the [detailed guide] and [RFD 619]. +//! +//! [detailed guide]: https://github.com/oxidecomputer/dropshot-api-manager/blob/main/guides/new-version.md +//! [RFD 619]: https://rfd.shared.oxide.computer/rfd/619 + +mod impls; +pub mod latest; +#[path = "initial/mod.rs"] +pub mod v1; diff --git a/pantry/src/pantry.rs b/pantry/src/pantry.rs index d61397447..7fea06c5e 100644 --- a/pantry/src/pantry.rs +++ b/pantry/src/pantry.rs @@ -27,7 +27,9 @@ use crucible::Volume; use crucible::VolumeConstructionRequest; use crucible_common::CrucibleError; use crucible_common::crucible_bail; -use crucible_pantry_types::{ExpectedDigest, PantryStatus, VolumeStatus}; +use crucible_pantry_types::pantry::{ + ExpectedDigest, PantryStatus, VolumeStatus, +}; pub enum ActiveObservation { /// This Pantry has never seen this Volume active diff --git a/pantry/src/server.rs b/pantry/src/server.rs index 5059106cb..c06aa7ea6 100644 --- a/pantry/src/server.rs +++ b/pantry/src/server.rs @@ -6,8 +6,15 @@ use std::sync::Arc; use anyhow::{Result, anyhow}; use base64::{Engine, engine}; -use crucible_pantry_api::*; -use crucible_pantry_types::*; +use crucible_pantry_api::{CruciblePantryApi, crucible_pantry_api_mod}; +use crucible_pantry_types::pantry::{ + AttachBackgroundRequest, AttachRequest, AttachResult, BulkReadRequest, + BulkReadResponse, BulkWriteRequest, ImportFromUrlRequest, + ImportFromUrlResponse, JobPath, JobPollResponse, JobResultOkResponse, + PantryStatus, ReplaceRequest, ReplaceResult, ScrubResponse, + SnapshotRequest, ValidateRequest, ValidateResponse, VolumePath, + VolumeStatus, +}; use dropshot::{ ClientSpecifiesVersionInHeader, HandlerTaskMode, HttpError, HttpResponseDeleted, HttpResponseOk, HttpResponseUpdatedNoContent, @@ -84,7 +91,7 @@ impl CruciblePantryApi for CruciblePantryImpl { rqctx: RequestContext, path: TypedPath, body: TypedBody, - ) -> SResult, HttpError> { + ) -> SResult, HttpError> { let path = path.into_inner(); let body = body.into_inner(); let pantry = rqctx.context();