diff --git a/Cargo.lock b/Cargo.lock index 2e7dd4f1484..29a43b2fb06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -953,6 +953,7 @@ name = "bootstrap-agent-api" version = "0.1.0" dependencies = [ "dropshot", + "dropshot-api-manager-types", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -5262,6 +5263,7 @@ version = "0.1.0" dependencies = [ "anyhow", "dropshot", + "dropshot-api-manager-types", "hyper", "omicron-common", "omicron-uuid-kinds", @@ -6748,6 +6750,7 @@ name = "nexus-internal-api" version = "0.1.0" dependencies = [ "dropshot", + "dropshot-api-manager-types", "http", "nexus-types", "omicron-common", @@ -7271,6 +7274,7 @@ dependencies = [ "illumos-utils", "internal-dns-resolver", "internal-dns-types", + "nexus-client", "nexus-config", "nexus-db-queries", "nexus-lockstep-client", @@ -8161,6 +8165,7 @@ dependencies = [ "mg-admin-client", "nexus-auth", "nexus-background-task-interface", + "nexus-client", "nexus-config", "nexus-db-lookup", "nexus-db-model", @@ -8554,6 +8559,7 @@ dependencies = [ "dropshot", "futures", "libc", + "omicron-common", "omicron-workspace-hack", "oxide-tokio-rt", "repo-depot-api", @@ -11348,6 +11354,7 @@ name = "repo-depot-api" version = "0.1.0" dependencies = [ "dropshot", + "dropshot-api-manager-types", "omicron-workspace-hack", "schemars 0.8.22", "serde", diff --git a/clients/bootstrap-agent-client/src/lib.rs b/clients/bootstrap-agent-client/src/lib.rs index 5a893bfc40c..232bd08742e 100644 --- a/clients/bootstrap-agent-client/src/lib.rs +++ b/clients/bootstrap-agent-client/src/lib.rs @@ -5,7 +5,7 @@ //! Interface for making API requests to a Bootstrap Agent progenitor::generate_api!( - spec = "../../openapi/bootstrap-agent.json", + spec = "../../openapi/bootstrap-agent/bootstrap-agent-1.0.0-127591.json", interface = Positional, inner_type = slog::Logger, pre_hook = (|log: &slog::Logger, request: &reqwest::Request| { diff --git a/clients/installinator-client/src/lib.rs b/clients/installinator-client/src/lib.rs index 8e91bb08235..802c503465e 100644 --- a/clients/installinator-client/src/lib.rs +++ b/clients/installinator-client/src/lib.rs @@ -5,7 +5,7 @@ //! Interface for installinator to make API requests. progenitor::generate_api!( - spec = "../../openapi/installinator.json", + spec = "../../openapi/installinator/installinator-1.0.0-c0ed87.json", interface = Positional, inner_type = slog::Logger, pre_hook = (|log: &slog::Logger, request: &reqwest::Request| { diff --git a/clients/nexus-client/src/lib.rs b/clients/nexus-client/src/lib.rs index 9751231a688..d5c73f5a0c2 100644 --- a/clients/nexus-client/src/lib.rs +++ b/clients/nexus-client/src/lib.rs @@ -6,7 +6,7 @@ //! from within the control plane progenitor::generate_api!( - spec = "../../openapi/nexus-internal.json", + spec = "../../openapi/nexus-internal/nexus-internal-1.0.0-6d8ade.json", interface = Positional, derives = [schemars::JsonSchema, PartialEq], inner_type = slog::Logger, @@ -261,3 +261,13 @@ impl TryFrom }) } } + +impl From for types::Baseboard { + fn from(value: nexus_types::external_api::shared::Baseboard) -> Self { + types::Baseboard { + part: value.part, + revision: value.revision, + serial: value.serial, + } + } +} diff --git a/clients/repo-depot-client/src/lib.rs b/clients/repo-depot-client/src/lib.rs index 85f6d1318f6..439f77b778f 100644 --- a/clients/repo-depot-client/src/lib.rs +++ b/clients/repo-depot-client/src/lib.rs @@ -5,7 +5,7 @@ //! Interface for Sled Agent's Repo Depot to make API requests. progenitor::generate_api!( - spec = "../../openapi/repo-depot.json", + spec = "../../openapi/repo-depot/repo-depot-1.0.0-65083f.json", interface = Positional, inner_type = slog::Logger, pre_hook = (|log: &slog::Logger, request: &reqwest::Request| { diff --git a/dev-tools/dropshot-apis/src/main.rs b/dev-tools/dropshot-apis/src/main.rs index a975260b3b1..73ddf87c2be 100644 --- a/dev-tools/dropshot-apis/src/main.rs +++ b/dev-tools/dropshot-apis/src/main.rs @@ -53,7 +53,9 @@ fn all_apis() -> anyhow::Result { let apis = vec![ ManagedApiConfig { title: "Bootstrap Agent API", - versions: Versions::new_lockstep(semver::Version::new(0, 0, 1)), + versions: Versions::new_versioned( + bootstrap_agent_api::supported_versions(), + ), metadata: ManagedApiMetadata { description: Some("Per-sled API for setup and teardown"), contact_url: Some("https://oxide.computer"), @@ -172,7 +174,9 @@ fn all_apis() -> anyhow::Result { }, ManagedApiConfig { title: "Installinator API", - versions: Versions::new_lockstep(semver::Version::new(0, 0, 1)), + versions: Versions::new_versioned( + installinator_api::supported_versions(), + ), metadata: ManagedApiMetadata { description: Some( "API for installinator to fetch artifacts \ @@ -205,7 +209,9 @@ fn all_apis() -> anyhow::Result { }, ManagedApiConfig { title: "Nexus internal API", - versions: Versions::new_lockstep(semver::Version::new(0, 0, 1)), + versions: Versions::new_versioned( + nexus_internal_api::supported_versions(), + ), metadata: ManagedApiMetadata { description: Some("Nexus internal API"), contact_url: Some("https://oxide.computer"), @@ -261,7 +267,9 @@ fn all_apis() -> anyhow::Result { }, ManagedApiConfig { title: "Oxide TUF Repo Depot API", - versions: Versions::new_lockstep(semver::Version::new(0, 0, 1)), + versions: Versions::new_versioned( + repo_depot_api::supported_versions(), + ), metadata: ManagedApiMetadata { description: Some("API for fetching update artifacts"), contact_url: Some("https://oxide.computer"), diff --git a/dev-tools/repo-depot-standalone/Cargo.toml b/dev-tools/repo-depot-standalone/Cargo.toml index 644f5373427..ee8be48efed 100644 --- a/dev-tools/repo-depot-standalone/Cargo.toml +++ b/dev-tools/repo-depot-standalone/Cargo.toml @@ -16,6 +16,8 @@ clap.workspace = true dropshot.workspace = true futures.workspace = true libc.workspace = true +omicron-common.workspace = true +omicron-workspace-hack.workspace = true oxide-tokio-rt.workspace = true repo-depot-api.workspace = true serde_json.workspace = true @@ -26,7 +28,6 @@ tokio = { workspace = true, features = [ "full" ] } tokio-util.workspace = true tufaceous-artifact.workspace = true update-common.workspace = true -omicron-workspace-hack.workspace = true [[bin]] name = "repo-depot-standalone" diff --git a/dev-tools/repo-depot-standalone/src/main.rs b/dev-tools/repo-depot-standalone/src/main.rs index 78314ffc7ba..852f1f9535d 100644 --- a/dev-tools/repo-depot-standalone/src/main.rs +++ b/dev-tools/repo-depot-standalone/src/main.rs @@ -124,6 +124,12 @@ impl RepoDepotStandalone { bind_address: self.listen_addr, ..Default::default() }) + .version_policy(dropshot::VersionPolicy::Dynamic(Box::new( + dropshot::ClientSpecifiesVersionInHeader::new( + omicron_common::api::VERSION_HEADER, + repo_depot_api::latest_version(), + ), + ))) .start() .context("failed to create server")?; diff --git a/installinator-api/Cargo.toml b/installinator-api/Cargo.toml index f6b10268b3a..d142379b2e5 100644 --- a/installinator-api/Cargo.toml +++ b/installinator-api/Cargo.toml @@ -10,6 +10,7 @@ workspace = true [dependencies] anyhow.workspace = true dropshot.workspace = true +dropshot-api-manager-types.workspace = true hyper.workspace = true omicron-common.workspace = true omicron-uuid-kinds.workspace = true diff --git a/installinator-api/src/lib.rs b/installinator-api/src/lib.rs index 98c4e67820b..c42379ece4e 100644 --- a/installinator-api/src/lib.rs +++ b/installinator-api/src/lib.rs @@ -14,6 +14,7 @@ use dropshot::{ HttpResponseHeaders, HttpResponseOk, HttpResponseUpdatedNoContent, Path, RequestContext, TypedBody, }; +use dropshot_api_manager_types::api_versions; use hyper::header; use omicron_uuid_kinds::MupdateUuid; use schemars::JsonSchema; @@ -21,6 +22,12 @@ use serde::Deserialize; use tufaceous_artifact::ArtifactHashId; use update_engine::{NestedSpec, events::EventReport}; +api_versions!([ + // Do not create new versions of this client-side versioned API. + // https://github.com/oxidecomputer/omicron/issues/9290 + (1, INITIAL), +]); + const PROGRESS_REPORT_MAX_BYTES: usize = 4 * 1024 * 1024; #[derive(Debug, Deserialize, JsonSchema)] diff --git a/nexus/Cargo.toml b/nexus/Cargo.toml index 7c752b11cf9..3f4180bebbb 100644 --- a/nexus/Cargo.toml +++ b/nexus/Cargo.toml @@ -160,6 +160,7 @@ httpmock.workspace = true httptest.workspace = true hubtools.workspace = true hyper-rustls.workspace = true +nexus-client.workspace = true nexus-db-queries = { workspace = true, features = ["testing"] } nexus-lockstep-client.workspace = true nexus-test-utils.workspace = true diff --git a/nexus/internal-api/Cargo.toml b/nexus/internal-api/Cargo.toml index ef6a9c6e464..dd23f5451f2 100644 --- a/nexus/internal-api/Cargo.toml +++ b/nexus/internal-api/Cargo.toml @@ -9,11 +9,12 @@ workspace = true [dependencies] dropshot.workspace = true +dropshot-api-manager-types.workspace = true http.workspace = true nexus-types.workspace = true omicron-common.workspace = true omicron-uuid-kinds.workspace = true omicron-workspace-hack.workspace = true -serde.workspace = true schemars.workspace = true +serde.workspace = true uuid.workspace = true diff --git a/nexus/internal-api/src/lib.rs b/nexus/internal-api/src/lib.rs index 4cf4b254772..fbbaffb7280 100644 --- a/nexus/internal-api/src/lib.rs +++ b/nexus/internal-api/src/lib.rs @@ -9,6 +9,7 @@ use dropshot::{ HttpResponseUpdatedNoContent, Path, Query, RequestContext, ResultsPage, TypedBody, }; +use dropshot_api_manager_types::api_versions; use nexus_types::{ external_api::{ shared::ProbeInfo, @@ -34,6 +35,12 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; +api_versions!([ + // Do not create new versions of this client-side versioned API. + // https://github.com/oxidecomputer/omicron/issues/9290 + (1, INITIAL), +]); + #[dropshot::api_description] pub trait NexusInternalApi { type Context; diff --git a/nexus/mgs-updates/src/test_util/test_artifacts.rs b/nexus/mgs-updates/src/test_util/test_artifacts.rs index 781d55c3497..5ffe2e61de4 100644 --- a/nexus/mgs-updates/src/test_util/test_artifacts.rs +++ b/nexus/mgs-updates/src/test_util/test_artifacts.rs @@ -193,6 +193,12 @@ impl TestArtifacts { .unwrap(); ServerBuilder::new(my_api, Arc::new(artifact_data), log) + .version_policy(dropshot::VersionPolicy::Dynamic(Box::new( + dropshot::ClientSpecifiesVersionInHeader::new( + omicron_common::api::VERSION_HEADER, + repo_depot_api::latest_version(), + ), + ))) .start() .context("failed to create server")? }; diff --git a/nexus/src/lib.rs b/nexus/src/lib.rs index 134e9050738..a9524bb952d 100644 --- a/nexus/src/lib.rs +++ b/nexus/src/lib.rs @@ -106,6 +106,12 @@ impl InternalServer { log.new(o!("component" => "dropshot_internal")), ) .config(config.deployment.dropshot_internal.clone()) + .version_policy(dropshot::VersionPolicy::Dynamic(Box::new( + dropshot::ClientSpecifiesVersionInHeader::new( + omicron_common::api::VERSION_HEADER, + nexus_internal_api::latest_version(), + ), + ))) .start() .map_err(|error| format!("initializing internal server: {}", error)) { diff --git a/nexus/test-utils/Cargo.toml b/nexus/test-utils/Cargo.toml index 0423823fc6c..5c84ccff88f 100644 --- a/nexus/test-utils/Cargo.toml +++ b/nexus/test-utils/Cargo.toml @@ -29,6 +29,7 @@ id-map.workspace = true illumos-utils.workspace = true internal-dns-resolver.workspace = true internal-dns-types.workspace = true +nexus-client.workspace = true nexus-config.workspace = true nexus-db-queries = { workspace = true, features = [ "testing" ] } nexus-lockstep-client.workspace = true diff --git a/nexus/test-utils/src/lib.rs b/nexus/test-utils/src/lib.rs index 4020c4a7391..a208ed1d420 100644 --- a/nexus/test-utils/src/lib.rs +++ b/nexus/test-utils/src/lib.rs @@ -271,6 +271,13 @@ impl ControlPlaneTestContext { } } + pub fn internal_client(&self) -> nexus_client::Client { + nexus_client::Client::new( + &format!("http://{}", self.internal_client.bind_address), + self.internal_client.client_log.clone(), + ) + } + pub async fn teardown(mut self) { self.server.close().await; self.database.cleanup().await.unwrap(); diff --git a/nexus/tests/integration_tests/crucible_replacements.rs b/nexus/tests/integration_tests/crucible_replacements.rs index 657c5644cbe..35f8195f89c 100644 --- a/nexus/tests/integration_tests/crucible_replacements.rs +++ b/nexus/tests/integration_tests/crucible_replacements.rs @@ -1314,7 +1314,7 @@ mod region_snapshot_replacement { datastore: Arc, disk_test: DiskTest<'a>, client: ClientTestContext, - internal_client: ClientTestContext, + internal_client: nexus_client::Client, lockstep_client: ClientTestContext, replacement_request_id: Uuid, snapshot_socket_addr: SocketAddr, @@ -1334,7 +1334,7 @@ mod region_snapshot_replacement { .await; let client = &cptestctx.external_client; - let internal_client = &cptestctx.internal_client; + let internal_client = cptestctx.internal_client(); let lockstep_client = &cptestctx.lockstep_client; let datastore = nexus.datastore().clone(); @@ -1768,15 +1768,8 @@ mod region_snapshot_replacement { let disk_id = disk_from_snapshot.identity.id; - // Note: `make_request` needs a type here, otherwise rustc cannot - // figure out the type of the `request_body` parameter self.internal_client - .make_request::( - http::Method::POST, - &format!("/disk/{disk_id}/remove-read-only-parent"), - None, - http::StatusCode::NO_CONTENT, - ) + .cpapi_disk_remove_read_only_parent(&disk_id) .await .unwrap(); } diff --git a/nexus/tests/integration_tests/rack.rs b/nexus/tests/integration_tests/rack.rs index f2f01b48a47..3c29591500e 100644 --- a/nexus/tests/integration_tests/rack.rs +++ b/nexus/tests/integration_tests/rack.rs @@ -10,8 +10,6 @@ use nexus_db_model::SledCpuFamily as DbSledCpuFamily; use nexus_db_model::SledSystemHardware; use nexus_db_model::SledUpdate; use nexus_lockstep_client::types::SledId; -use nexus_sled_agent_shared::inventory::SledCpuFamily; -use nexus_sled_agent_shared::inventory::SledRole; use nexus_test_utils::TEST_SUITE_PASSWORD; use nexus_test_utils::http_testing::AuthnMode; use nexus_test_utils::http_testing::NexusRequest; @@ -21,11 +19,10 @@ use nexus_test_utils_macros::nexus_test; use nexus_types::external_api::params; use nexus_types::external_api::shared::UninitializedSled; use nexus_types::external_api::views::Rack; -use nexus_types::internal_api::params::SledAgentInfo; use omicron_common::api::external::ByteCount; use omicron_common::api::external::Generation; +use omicron_uuid_kinds::SledUuid; use std::time::Duration; -use uuid::Uuid; type ControlPlaneTestContext = nexus_test_utils::ControlPlaneTestContext; @@ -106,7 +103,7 @@ async fn test_sled_list_uninitialized(cptestctx: &ControlPlaneTestContext) { .wait_for_at_least_one_inventory_collection(Duration::from_secs(60)) .await; - let internal_client = &cptestctx.internal_client; + let internal_client = cptestctx.internal_client(); let external_client = &cptestctx.external_client; let list_url = "/v1/system/hardware/sleds-uninitialized"; let mut uninitialized_sleds = @@ -127,28 +124,20 @@ async fn test_sled_list_uninitialized(cptestctx: &ControlPlaneTestContext) { // Insert one of these fake sleds into the `sled` table. // Just pick some random fields other than `baseboard` let baseboard = uninitialized_sleds.pop().unwrap().baseboard; - let sled_uuid = Uuid::new_v4(); - let sa = SledAgentInfo { + let sled_uuid = SledUuid::new_v4(); + let sa = nexus_client::types::SledAgentInfo { sa_address: "[fd00:1122:3344:0100::1]:8080".parse().unwrap(), repo_depot_port: 8081, - role: SledRole::Gimlet, - baseboard, + role: nexus_client::types::SledRole::Gimlet, + baseboard: baseboard.into(), usable_hardware_threads: 32, - usable_physical_ram: ByteCount::from_gibibytes_u32(100), - reservoir_size: ByteCount::from_mebibytes_u32(100), - cpu_family: SledCpuFamily::Unknown, + usable_physical_ram: ByteCount::from_gibibytes_u32(100).into(), + reservoir_size: ByteCount::from_mebibytes_u32(100).into(), + cpu_family: nexus_client::types::SledCpuFamily::Unknown, generation: Generation::new(), decommissioned: false, }; - internal_client - .make_request( - Method::POST, - format!("/sled-agents/{sled_uuid}").as_str(), - Some(&sa), - StatusCode::NO_CONTENT, - ) - .await - .unwrap(); + internal_client.sled_agent_put(&sled_uuid, &sa).await.unwrap(); // Ensure there's only one unintialized sled remaining, and it's not // the one that was just added into the `sled` table diff --git a/nexus/tests/integration_tests/volume_management.rs b/nexus/tests/integration_tests/volume_management.rs index 5975a51d7f7..806e1aa9d61 100644 --- a/nexus/tests/integration_tests/volume_management.rs +++ b/nexus/tests/integration_tests/volume_management.rs @@ -58,7 +58,6 @@ use omicron_common::api::external::ByteCount; use omicron_common::api::external::Disk; use omicron_common::api::external::IdentityMetadataCreateParams; use omicron_common::api::external::Name; -use omicron_common::api::internal; use omicron_test_utils::dev::poll::CondCheckError; use omicron_test_utils::dev::poll::wait_for_condition; use omicron_uuid_kinds::DatasetUuid; @@ -1614,19 +1613,10 @@ async fn test_volume_remove_rop_saga(cptestctx: &ControlPlaneTestContext) { println!("Created this volume: {:?}", volume_id); // disk to volume id, to then remove ROP? - let int_client = &cptestctx.internal_client; - let rop_url = format!("/volume/{}/remove-read-only-parent", volume_id); + let int_client = cptestctx.internal_client(); // Call the internal API endpoint for removal of the read only parent - int_client - .make_request( - Method::POST, - &rop_url, - None as Option<&serde_json::Value>, - StatusCode::NO_CONTENT, - ) - .await - .unwrap(); + int_client.cpapi_volume_remove_read_only_parent(&volume_id).await.unwrap(); let new_vol = datastore .volume_checkout( @@ -1677,17 +1667,11 @@ async fn test_volume_remove_rop_saga_twice( println!("Created this volume: {:?}", volume_id); // disk to volume id, to then remove ROP? - let int_client = &cptestctx.internal_client; - let rop_url = format!("/volume/{}/remove-read-only-parent", volume_id); + let int_client = cptestctx.internal_client(); // Call the internal API endpoint for removal of the read only parent let res = int_client - .make_request( - Method::POST, - &rop_url, - None as Option<&serde_json::Value>, - StatusCode::NO_CONTENT, - ) + .cpapi_volume_remove_read_only_parent(&volume_id) .await .unwrap(); @@ -1718,12 +1702,7 @@ async fn test_volume_remove_rop_saga_twice( // Call the internal API endpoint a second time. Should be okay. let res = int_client - .make_request( - Method::POST, - &rop_url, - None as Option<&serde_json::Value>, - StatusCode::NO_CONTENT, - ) + .cpapi_volume_remove_read_only_parent(&volume_id) .await .unwrap(); @@ -1738,19 +1717,10 @@ async fn test_volume_remove_rop_saga_no_volume( let volume_id = VolumeUuid::new_v4(); println!("Non-existant volume: {:?}", volume_id); - let int_client = &cptestctx.internal_client; - let rop_url = format!("/volume/{}/remove-read-only-parent", volume_id); + let int_client = cptestctx.internal_client(); // Call the internal API endpoint for removal of the read only parent - int_client - .make_request( - Method::POST, - &rop_url, - None as Option<&serde_json::Value>, - StatusCode::NO_CONTENT, - ) - .await - .unwrap(); + int_client.cpapi_volume_remove_read_only_parent(&volume_id).await.unwrap(); } #[nexus_test] @@ -1775,20 +1745,10 @@ async fn test_volume_remove_rop_saga_volume_not_volume( .await .unwrap(); - let int_client = &cptestctx.internal_client; - // Call the saga on this volume - let rop_url = format!("/volume/{}/remove-read-only-parent", volume_id); + let int_client = cptestctx.internal_client(); // Call the internal API endpoint for removal of the read only parent - int_client - .make_request( - Method::POST, - &rop_url, - None as Option<&serde_json::Value>, - StatusCode::NO_CONTENT, - ) - .await - .unwrap(); + int_client.cpapi_volume_remove_read_only_parent(&volume_id).await.unwrap(); } #[nexus_test] @@ -1814,19 +1774,10 @@ async fn test_volume_remove_rop_saga_deleted_volume( // Soft delete the volume let _cr = datastore.soft_delete_volume(volume_id).await.unwrap(); - let int_client = &cptestctx.internal_client; - let rop_url = format!("/volume/{}/remove-read-only-parent", volume_id); + let int_client = cptestctx.internal_client(); // Call the internal API endpoint for removal of the read only parent - int_client - .make_request( - Method::POST, - &rop_url, - None as Option<&serde_json::Value>, - StatusCode::NO_CONTENT, - ) - .await - .unwrap(); + int_client.cpapi_volume_remove_read_only_parent(&volume_id).await.unwrap(); let new_vol = datastore .volume_checkout( @@ -2717,7 +2668,7 @@ async fn test_volume_hard_delete_idempotent( async fn test_upstairs_repair_notify_idempotent( cptestctx: &ControlPlaneTestContext, ) { - let int_client = &cptestctx.internal_client; + let int_client = cptestctx.internal_client(); let upstairs_id = UpstairsUuid::new_v4(); let session_id = UpstairsSessionUuid::new_v4(); @@ -2725,49 +2676,34 @@ async fn test_upstairs_repair_notify_idempotent( let region_id = DownstairsRegionUuid::new_v4(); // Send the same start request. - let notify_url = format!("/crucible/0/upstairs/{upstairs_id}/repair-start"); - - let request = internal::nexus::RepairStartInfo { + let request = nexus_client::types::RepairStartInfo { time: Utc::now(), session_id, repair_id, - repair_type: internal::nexus::UpstairsRepairType::Live, - repairs: vec![internal::nexus::DownstairsUnderRepair { + repair_type: nexus_client::types::UpstairsRepairType::Live, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345".parse().unwrap(), }], }; int_client - .make_request( - Method::POST, - ¬ify_url, - Some(request.clone()), - StatusCode::NO_CONTENT, - ) + .cpapi_upstairs_repair_start(&upstairs_id, &request) .await .unwrap(); int_client - .make_request( - Method::POST, - ¬ify_url, - Some(request), - StatusCode::NO_CONTENT, - ) + .cpapi_upstairs_repair_start(&upstairs_id, &request) .await .unwrap(); // Send the same finish request. - let notify_url = - format!("/crucible/0/upstairs/{upstairs_id}/repair-finish"); - - let request = internal::nexus::RepairFinishInfo { + let request = nexus_client::types::RepairFinishInfo { time: Utc::now(), session_id, repair_id, - repair_type: internal::nexus::UpstairsRepairType::Live, - repairs: vec![internal::nexus::DownstairsUnderRepair { + repair_type: nexus_client::types::UpstairsRepairType::Live, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345".parse().unwrap(), }], @@ -2775,22 +2711,12 @@ async fn test_upstairs_repair_notify_idempotent( }; int_client - .make_request( - Method::POST, - ¬ify_url, - Some(request.clone()), - StatusCode::NO_CONTENT, - ) + .cpapi_upstairs_repair_finish(&upstairs_id, &request) .await .unwrap(); int_client - .make_request( - Method::POST, - ¬ify_url, - Some(request), - StatusCode::NO_CONTENT, - ) + .cpapi_upstairs_repair_finish(&upstairs_id, &request) .await .unwrap(); } @@ -2801,59 +2727,53 @@ async fn test_upstairs_repair_notify_idempotent( async fn test_upstairs_repair_notify_different_finish_status( cptestctx: &ControlPlaneTestContext, ) { - let int_client = &cptestctx.internal_client; + let int_client = cptestctx.internal_client(); let upstairs_id = UpstairsUuid::new_v4(); let session_id = UpstairsSessionUuid::new_v4(); let repair_id = UpstairsRepairUuid::new_v4(); let region_id = DownstairsRegionUuid::new_v4(); - let notify_url = - format!("/crucible/0/upstairs/{upstairs_id}/repair-finish"); - int_client - .make_request( - Method::POST, - ¬ify_url, - Some(internal::nexus::RepairFinishInfo { + .cpapi_upstairs_repair_finish( + &upstairs_id, + &nexus_client::types::RepairFinishInfo { time: Utc::now(), session_id, repair_id, - repair_type: internal::nexus::UpstairsRepairType::Live, - repairs: vec![internal::nexus::DownstairsUnderRepair { + repair_type: nexus_client::types::UpstairsRepairType::Live, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345" .parse() .unwrap(), }], aborted: false, // live repair was ok - }), - StatusCode::NO_CONTENT, + }, ) .await .unwrap(); - int_client - .make_request( - Method::POST, - ¬ify_url, - Some(internal::nexus::RepairFinishInfo { + let err = int_client + .cpapi_upstairs_repair_finish( + &upstairs_id, + &nexus_client::types::RepairFinishInfo { time: Utc::now(), session_id, repair_id, - repair_type: internal::nexus::UpstairsRepairType::Live, - repairs: vec![internal::nexus::DownstairsUnderRepair { + repair_type: nexus_client::types::UpstairsRepairType::Live, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345" .parse() .unwrap(), }], aborted: true, // live repair failed? - }), - StatusCode::CONFLICT, + }, ) .await .unwrap_err(); + assert_eq!(err.status(), Some(StatusCode::CONFLICT)); } /// Test that the same Upstairs can rerun a repair again. @@ -2861,7 +2781,7 @@ async fn test_upstairs_repair_notify_different_finish_status( async fn test_upstairs_repair_same_upstairs_retry( cptestctx: &ControlPlaneTestContext, ) { - let int_client = &cptestctx.internal_client; + let int_client = cptestctx.internal_client(); let upstairs_id = UpstairsUuid::new_v4(); let session_id = UpstairsSessionUuid::new_v4(); @@ -2870,50 +2790,41 @@ async fn test_upstairs_repair_same_upstairs_retry( // Simulate one failed repair - let notify_start_url = - format!("/crucible/0/upstairs/{upstairs_id}/repair-start"); - let notify_finish_url = - format!("/crucible/0/upstairs/{upstairs_id}/repair-finish"); - int_client - .make_request( - Method::POST, - ¬ify_start_url, - Some(internal::nexus::RepairStartInfo { + .cpapi_upstairs_repair_start( + &upstairs_id, + &nexus_client::types::RepairStartInfo { time: Utc::now(), session_id, repair_id, - repair_type: internal::nexus::UpstairsRepairType::Live, - repairs: vec![internal::nexus::DownstairsUnderRepair { + repair_type: nexus_client::types::UpstairsRepairType::Live, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345" .parse() .unwrap(), }], - }), - StatusCode::NO_CONTENT, + }, ) .await .unwrap(); int_client - .make_request( - Method::POST, - ¬ify_finish_url, - Some(internal::nexus::RepairFinishInfo { + .cpapi_upstairs_repair_finish( + &upstairs_id, + &nexus_client::types::RepairFinishInfo { time: Utc::now(), session_id, repair_id, - repair_type: internal::nexus::UpstairsRepairType::Live, - repairs: vec![internal::nexus::DownstairsUnderRepair { + repair_type: nexus_client::types::UpstairsRepairType::Live, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345" .parse() .unwrap(), }], aborted: true, - }), - StatusCode::NO_CONTENT, + }, ) .await .unwrap(); @@ -2923,44 +2834,40 @@ async fn test_upstairs_repair_same_upstairs_retry( let repair_id = UpstairsRepairUuid::new_v4(); int_client - .make_request( - Method::POST, - ¬ify_start_url, - Some(internal::nexus::RepairStartInfo { + .cpapi_upstairs_repair_start( + &upstairs_id, + &nexus_client::types::RepairStartInfo { time: Utc::now(), session_id, repair_id, - repair_type: internal::nexus::UpstairsRepairType::Live, - repairs: vec![internal::nexus::DownstairsUnderRepair { + repair_type: nexus_client::types::UpstairsRepairType::Live, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345" .parse() .unwrap(), }], - }), - StatusCode::NO_CONTENT, + }, ) .await .unwrap(); int_client - .make_request( - Method::POST, - ¬ify_finish_url, - Some(internal::nexus::RepairFinishInfo { + .cpapi_upstairs_repair_finish( + &upstairs_id, + &nexus_client::types::RepairFinishInfo { time: Utc::now(), session_id, repair_id, - repair_type: internal::nexus::UpstairsRepairType::Live, - repairs: vec![internal::nexus::DownstairsUnderRepair { + repair_type: nexus_client::types::UpstairsRepairType::Live, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345" .parse() .unwrap(), }], aborted: false, - }), - StatusCode::NO_CONTENT, + }, ) .await .unwrap(); @@ -2971,7 +2878,7 @@ async fn test_upstairs_repair_same_upstairs_retry( async fn test_upstairs_repair_different_upstairs_retry( cptestctx: &ControlPlaneTestContext, ) { - let int_client = &cptestctx.internal_client; + let int_client = cptestctx.internal_client(); let upstairs_id = UpstairsUuid::new_v4(); let session_id = UpstairsSessionUuid::new_v4(); @@ -2980,50 +2887,41 @@ async fn test_upstairs_repair_different_upstairs_retry( // Simulate one failed repair by one Upstairs - let notify_start_url = - format!("/crucible/0/upstairs/{upstairs_id}/repair-start"); - let notify_finish_url = - format!("/crucible/0/upstairs/{upstairs_id}/repair-finish"); - int_client - .make_request( - Method::POST, - ¬ify_start_url, - Some(internal::nexus::RepairStartInfo { + .cpapi_upstairs_repair_start( + &upstairs_id, + &nexus_client::types::RepairStartInfo { time: Utc::now(), session_id, repair_id, - repair_type: internal::nexus::UpstairsRepairType::Live, - repairs: vec![internal::nexus::DownstairsUnderRepair { + repair_type: nexus_client::types::UpstairsRepairType::Live, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345" .parse() .unwrap(), }], - }), - StatusCode::NO_CONTENT, + }, ) .await .unwrap(); int_client - .make_request( - Method::POST, - ¬ify_finish_url, - Some(internal::nexus::RepairFinishInfo { + .cpapi_upstairs_repair_finish( + &upstairs_id, + &nexus_client::types::RepairFinishInfo { time: Utc::now(), session_id, repair_id, - repair_type: internal::nexus::UpstairsRepairType::Live, - repairs: vec![internal::nexus::DownstairsUnderRepair { + repair_type: nexus_client::types::UpstairsRepairType::Live, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345" .parse() .unwrap(), }], aborted: true, - }), - StatusCode::NO_CONTENT, + }, ) .await .unwrap(); @@ -3034,44 +2932,40 @@ async fn test_upstairs_repair_different_upstairs_retry( let repair_id = UpstairsRepairUuid::new_v4(); int_client - .make_request( - Method::POST, - ¬ify_start_url, - Some(internal::nexus::RepairStartInfo { + .cpapi_upstairs_repair_start( + &upstairs_id, + &nexus_client::types::RepairStartInfo { time: Utc::now(), session_id, repair_id, - repair_type: internal::nexus::UpstairsRepairType::Live, - repairs: vec![internal::nexus::DownstairsUnderRepair { + repair_type: nexus_client::types::UpstairsRepairType::Live, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345" .parse() .unwrap(), }], - }), - StatusCode::NO_CONTENT, + }, ) .await .unwrap(); int_client - .make_request( - Method::POST, - ¬ify_finish_url, - Some(internal::nexus::RepairFinishInfo { + .cpapi_upstairs_repair_finish( + &upstairs_id, + &nexus_client::types::RepairFinishInfo { time: Utc::now(), session_id, repair_id, - repair_type: internal::nexus::UpstairsRepairType::Live, - repairs: vec![internal::nexus::DownstairsUnderRepair { + repair_type: nexus_client::types::UpstairsRepairType::Live, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345" .parse() .unwrap(), }], aborted: false, - }), - StatusCode::NO_CONTENT, + }, ) .await .unwrap(); @@ -3082,7 +2976,7 @@ async fn test_upstairs_repair_different_upstairs_retry( async fn test_upstairs_repair_different_upstairs_retry_interrupted( cptestctx: &ControlPlaneTestContext, ) { - let int_client = &cptestctx.internal_client; + let int_client = cptestctx.internal_client(); let upstairs_id = UpstairsUuid::new_v4(); let session_id = UpstairsSessionUuid::new_v4(); @@ -3092,28 +2986,21 @@ async fn test_upstairs_repair_different_upstairs_retry_interrupted( // Simulate one failed repair by one Upstairs, which was interrupted (which // leads to no finish message). - let notify_start_url = - format!("/crucible/0/upstairs/{upstairs_id}/repair-start"); - let notify_finish_url = - format!("/crucible/0/upstairs/{upstairs_id}/repair-finish"); - int_client - .make_request( - Method::POST, - ¬ify_start_url, - Some(internal::nexus::RepairStartInfo { + .cpapi_upstairs_repair_start( + &upstairs_id, + &nexus_client::types::RepairStartInfo { time: Utc::now(), session_id, repair_id, - repair_type: internal::nexus::UpstairsRepairType::Live, - repairs: vec![internal::nexus::DownstairsUnderRepair { + repair_type: nexus_client::types::UpstairsRepairType::Live, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345" .parse() .unwrap(), }], - }), - StatusCode::NO_CONTENT, + }, ) .await .unwrap(); @@ -3125,44 +3012,40 @@ async fn test_upstairs_repair_different_upstairs_retry_interrupted( let repair_id = UpstairsRepairUuid::new_v4(); int_client - .make_request( - Method::POST, - ¬ify_start_url, - Some(internal::nexus::RepairStartInfo { + .cpapi_upstairs_repair_start( + &upstairs_id, + &nexus_client::types::RepairStartInfo { time: Utc::now(), session_id, repair_id, - repair_type: internal::nexus::UpstairsRepairType::Live, - repairs: vec![internal::nexus::DownstairsUnderRepair { + repair_type: nexus_client::types::UpstairsRepairType::Live, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345" .parse() .unwrap(), }], - }), - StatusCode::NO_CONTENT, + }, ) .await .unwrap(); int_client - .make_request( - Method::POST, - ¬ify_finish_url, - Some(internal::nexus::RepairFinishInfo { + .cpapi_upstairs_repair_finish( + &upstairs_id, + &nexus_client::types::RepairFinishInfo { time: Utc::now(), session_id, repair_id, - repair_type: internal::nexus::UpstairsRepairType::Live, - repairs: vec![internal::nexus::DownstairsUnderRepair { + repair_type: nexus_client::types::UpstairsRepairType::Live, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345" .parse() .unwrap(), }], aborted: false, - }), - StatusCode::NO_CONTENT, + }, ) .await .unwrap(); @@ -3173,58 +3056,52 @@ async fn test_upstairs_repair_different_upstairs_retry_interrupted( async fn test_upstairs_repair_repair_id_and_type_conflict( cptestctx: &ControlPlaneTestContext, ) { - let int_client = &cptestctx.internal_client; + let int_client = cptestctx.internal_client(); let upstairs_id = UpstairsUuid::new_v4(); let session_id = UpstairsSessionUuid::new_v4(); let repair_id = UpstairsRepairUuid::new_v4(); let region_id = DownstairsRegionUuid::new_v4(); - let notify_start_url = - format!("/crucible/0/upstairs/{upstairs_id}/repair-start"); - int_client - .make_request( - Method::POST, - ¬ify_start_url, - Some(internal::nexus::RepairStartInfo { + .cpapi_upstairs_repair_start( + &upstairs_id, + &nexus_client::types::RepairStartInfo { time: Utc::now(), session_id, repair_id, - repair_type: internal::nexus::UpstairsRepairType::Live, - repairs: vec![internal::nexus::DownstairsUnderRepair { + repair_type: nexus_client::types::UpstairsRepairType::Live, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345" .parse() .unwrap(), }], - }), - StatusCode::NO_CONTENT, + }, ) .await .unwrap(); - int_client - .make_request( - Method::POST, - ¬ify_start_url, - Some(internal::nexus::RepairStartInfo { + let err = int_client + .cpapi_upstairs_repair_start( + &upstairs_id, + &nexus_client::types::RepairStartInfo { time: Utc::now(), session_id, repair_id, repair_type: - internal::nexus::UpstairsRepairType::Reconciliation, - repairs: vec![internal::nexus::DownstairsUnderRepair { + nexus_client::types::UpstairsRepairType::Reconciliation, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345" .parse() .unwrap(), }], - }), - StatusCode::CONFLICT, + }, ) .await .unwrap_err(); + assert_eq!(err.status(), Some(StatusCode::CONFLICT)); } /// Test that an Upstairs can submit progress for a repair @@ -3232,7 +3109,7 @@ async fn test_upstairs_repair_repair_id_and_type_conflict( async fn test_upstairs_repair_submit_progress( cptestctx: &ControlPlaneTestContext, ) { - let int_client = &cptestctx.internal_client; + let int_client = cptestctx.internal_client(); let upstairs_id = UpstairsUuid::new_v4(); let session_id = UpstairsSessionUuid::new_v4(); @@ -3241,45 +3118,35 @@ async fn test_upstairs_repair_submit_progress( // A repair must be started before progress can be submitted - let notify_start_url = - format!("/crucible/0/upstairs/{upstairs_id}/repair-start"); - int_client - .make_request( - Method::POST, - ¬ify_start_url, - Some(internal::nexus::RepairStartInfo { + .cpapi_upstairs_repair_start( + &upstairs_id, + &nexus_client::types::RepairStartInfo { time: Utc::now(), session_id, repair_id, - repair_type: internal::nexus::UpstairsRepairType::Live, - repairs: vec![internal::nexus::DownstairsUnderRepair { + repair_type: nexus_client::types::UpstairsRepairType::Live, + repairs: vec![nexus_client::types::DownstairsUnderRepair { region_uuid: region_id, target_addr: "[fd00:1122:3344:101::8]:12345" .parse() .unwrap(), }], - }), - StatusCode::NO_CONTENT, + }, ) .await .unwrap(); - let progress_url = format!( - "/crucible/0/upstairs/{upstairs_id}/repair/{repair_id}/progress" - ); - for i in 0..100 { int_client - .make_request( - Method::POST, - &progress_url, - Some(internal::nexus::RepairProgress { + .cpapi_upstairs_repair_progress( + &upstairs_id, + &repair_id, + &nexus_client::types::RepairProgress { time: Utc::now(), current_item: i, total_items: 100, - }), - StatusCode::NO_CONTENT, + }, ) .await .unwrap(); @@ -3291,28 +3158,24 @@ async fn test_upstairs_repair_submit_progress( async fn test_upstairs_repair_reject_submit_progress_when_no_repair( cptestctx: &ControlPlaneTestContext, ) { - let int_client = &cptestctx.internal_client; + let int_client = cptestctx.internal_client(); let upstairs_id = UpstairsUuid::new_v4(); let repair_id = UpstairsRepairUuid::new_v4(); - let progress_url = format!( - "/crucible/0/upstairs/{upstairs_id}/repair/{repair_id}/progress" - ); - - int_client - .make_request( - Method::POST, - &progress_url, - Some(internal::nexus::RepairProgress { + let err = int_client + .cpapi_upstairs_repair_progress( + &upstairs_id, + &repair_id, + &nexus_client::types::RepairProgress { time: Utc::now(), current_item: 10, total_items: 100, - }), - StatusCode::NOT_FOUND, + }, ) .await .unwrap_err(); + assert_eq!(err.status(), Some(StatusCode::NOT_FOUND)); } /// Test that an Upstairs can notify Nexus when a Downstairs client task is @@ -3321,74 +3184,66 @@ async fn test_upstairs_repair_reject_submit_progress_when_no_repair( async fn test_upstairs_notify_downstairs_client_stop_request( cptestctx: &ControlPlaneTestContext, ) { - let int_client = &cptestctx.internal_client; + let int_client = cptestctx.internal_client(); let upstairs_id = UpstairsUuid::new_v4(); let downstairs_id = DownstairsUuid::new_v4(); - let stop_request_url = format!( - "/crucible/0/upstairs/{upstairs_id}/downstairs/{downstairs_id}/stop-request" - ); - // Make sure an Upstairs can re-send the notification - let request = internal::nexus::DownstairsClientStopRequest { + let request = nexus_client::types::DownstairsClientStopRequest { time: Utc::now(), reason: - internal::nexus::DownstairsClientStopRequestReason::TooManyOutstandingJobs, + nexus_client::types::DownstairsClientStopRequestReason::TooManyOutstandingJobs, }; int_client - .make_request( - Method::POST, - &stop_request_url, - Some(request.clone()), - StatusCode::NO_CONTENT, + .cpapi_downstairs_client_stop_request( + &upstairs_id, + &downstairs_id, + &request, ) .await .unwrap(); int_client - .make_request( - Method::POST, - &stop_request_url, - Some(request), - StatusCode::NO_CONTENT, + .cpapi_downstairs_client_stop_request( + &upstairs_id, + &downstairs_id, + &request, ) .await .unwrap(); // The client can be requested to stop for the same reason a different time - let request = internal::nexus::DownstairsClientStopRequest { + let request = nexus_client::types::DownstairsClientStopRequest { time: Utc::now(), reason: - internal::nexus::DownstairsClientStopRequestReason::TooManyOutstandingJobs, + nexus_client::types::DownstairsClientStopRequestReason::TooManyOutstandingJobs, }; int_client - .make_request( - Method::POST, - &stop_request_url, - Some(request), - StatusCode::NO_CONTENT, + .cpapi_downstairs_client_stop_request( + &upstairs_id, + &downstairs_id, + &request, ) .await .unwrap(); // The client can also be requested to stop for a different reason - let request = internal::nexus::DownstairsClientStopRequest { + let request = nexus_client::types::DownstairsClientStopRequest { time: Utc::now(), - reason: internal::nexus::DownstairsClientStopRequestReason::IOError, + reason: nexus_client::types::DownstairsClientStopRequestReason::IOError, }; int_client - .make_request( - Method::POST, - &stop_request_url, - Some(request), - StatusCode::NO_CONTENT, + .cpapi_downstairs_client_stop_request( + &upstairs_id, + &downstairs_id, + &request, ) .await .unwrap(); @@ -3399,73 +3254,49 @@ async fn test_upstairs_notify_downstairs_client_stop_request( async fn test_upstairs_notify_downstairs_client_stops( cptestctx: &ControlPlaneTestContext, ) { - let int_client = &cptestctx.internal_client; + let int_client = cptestctx.internal_client(); let upstairs_id = UpstairsUuid::new_v4(); let downstairs_id = DownstairsUuid::new_v4(); - let stopped_url = format!( - "/crucible/0/upstairs/{upstairs_id}/downstairs/{downstairs_id}/stopped" - ); - // Make sure an Upstairs can re-send the notification - let request = internal::nexus::DownstairsClientStopped { + let request = nexus_client::types::DownstairsClientStopped { time: Utc::now(), - reason: internal::nexus::DownstairsClientStoppedReason::ReadFailed, + reason: nexus_client::types::DownstairsClientStoppedReason::ReadFailed, }; int_client - .make_request( - Method::POST, - &stopped_url, - Some(request.clone()), - StatusCode::NO_CONTENT, - ) + .cpapi_downstairs_client_stopped(&upstairs_id, &downstairs_id, &request) .await .unwrap(); int_client - .make_request( - Method::POST, - &stopped_url, - Some(request), - StatusCode::NO_CONTENT, - ) + .cpapi_downstairs_client_stopped(&upstairs_id, &downstairs_id, &request) .await .unwrap(); // The client can stop for the same reason a different time - let request = internal::nexus::DownstairsClientStopped { + let request = nexus_client::types::DownstairsClientStopped { time: Utc::now(), - reason: internal::nexus::DownstairsClientStoppedReason::ReadFailed, + reason: nexus_client::types::DownstairsClientStoppedReason::ReadFailed, }; int_client - .make_request( - Method::POST, - &stopped_url, - Some(request), - StatusCode::NO_CONTENT, - ) + .cpapi_downstairs_client_stopped(&upstairs_id, &downstairs_id, &request) .await .unwrap(); // The client can also stop for a different reason - let request = internal::nexus::DownstairsClientStopped { + let request = nexus_client::types::DownstairsClientStopped { time: Utc::now(), - reason: internal::nexus::DownstairsClientStoppedReason::Timeout, + reason: nexus_client::types::DownstairsClientStoppedReason::Timeout, }; int_client - .make_request( - Method::POST, - &stopped_url, - Some(request), - StatusCode::NO_CONTENT, - ) + .cpapi_downstairs_client_stopped(&upstairs_id, &downstairs_id, &request) .await .unwrap(); } diff --git a/openapi/bootstrap-agent.json b/openapi/bootstrap-agent/bootstrap-agent-1.0.0-127591.json similarity index 99% rename from openapi/bootstrap-agent.json rename to openapi/bootstrap-agent/bootstrap-agent-1.0.0-127591.json index 746d94553b4..05d3b6589b7 100644 --- a/openapi/bootstrap-agent.json +++ b/openapi/bootstrap-agent/bootstrap-agent-1.0.0-127591.json @@ -7,7 +7,7 @@ "url": "https://oxide.computer", "email": "api@oxide.computer" }, - "version": "0.0.1" + "version": "1.0.0" }, "paths": { "/baseboard": { diff --git a/openapi/bootstrap-agent/bootstrap-agent-latest.json b/openapi/bootstrap-agent/bootstrap-agent-latest.json new file mode 120000 index 00000000000..6355e3b64f8 --- /dev/null +++ b/openapi/bootstrap-agent/bootstrap-agent-latest.json @@ -0,0 +1 @@ +bootstrap-agent-1.0.0-127591.json \ No newline at end of file diff --git a/openapi/installinator.json b/openapi/installinator/installinator-1.0.0-c0ed87.json similarity index 99% rename from openapi/installinator.json rename to openapi/installinator/installinator-1.0.0-c0ed87.json index f28b6e66fd5..b03b9dcd074 100644 --- a/openapi/installinator.json +++ b/openapi/installinator/installinator-1.0.0-c0ed87.json @@ -7,7 +7,7 @@ "url": "https://oxide.computer", "email": "api@oxide.computer" }, - "version": "0.0.1" + "version": "1.0.0" }, "paths": { "/artifacts/by-hash/{kind}/{hash}": { diff --git a/openapi/installinator/installinator-latest.json b/openapi/installinator/installinator-latest.json new file mode 120000 index 00000000000..e06d46dbcc3 --- /dev/null +++ b/openapi/installinator/installinator-latest.json @@ -0,0 +1 @@ +installinator-1.0.0-c0ed87.json \ No newline at end of file diff --git a/openapi/nexus-internal.json b/openapi/nexus-internal/nexus-internal-1.0.0-6d8ade.json similarity index 99% rename from openapi/nexus-internal.json rename to openapi/nexus-internal/nexus-internal-1.0.0-6d8ade.json index e2eabe99a41..515e2f96664 100644 --- a/openapi/nexus-internal.json +++ b/openapi/nexus-internal/nexus-internal-1.0.0-6d8ade.json @@ -7,7 +7,7 @@ "url": "https://oxide.computer", "email": "api@oxide.computer" }, - "version": "0.0.1" + "version": "1.0.0" }, "paths": { "/crucible/0/upstairs/{upstairs_id}/downstairs/{downstairs_id}/stop-request": { diff --git a/openapi/nexus-internal/nexus-internal-latest.json b/openapi/nexus-internal/nexus-internal-latest.json new file mode 120000 index 00000000000..dd2260a5a67 --- /dev/null +++ b/openapi/nexus-internal/nexus-internal-latest.json @@ -0,0 +1 @@ +nexus-internal-1.0.0-6d8ade.json \ No newline at end of file diff --git a/openapi/repo-depot.json b/openapi/repo-depot/repo-depot-1.0.0-65083f.json similarity index 98% rename from openapi/repo-depot.json rename to openapi/repo-depot/repo-depot-1.0.0-65083f.json index 0c0019cf8dc..7a572f9bffd 100644 --- a/openapi/repo-depot.json +++ b/openapi/repo-depot/repo-depot-1.0.0-65083f.json @@ -7,7 +7,7 @@ "url": "https://oxide.computer", "email": "api@oxide.computer" }, - "version": "0.0.1" + "version": "1.0.0" }, "paths": { "/artifact/sha256/{sha256}": { diff --git a/openapi/repo-depot/repo-depot-latest.json b/openapi/repo-depot/repo-depot-latest.json new file mode 120000 index 00000000000..fa002fb35c5 --- /dev/null +++ b/openapi/repo-depot/repo-depot-latest.json @@ -0,0 +1 @@ +repo-depot-1.0.0-65083f.json \ No newline at end of file diff --git a/sled-agent/bootstrap-agent-api/Cargo.toml b/sled-agent/bootstrap-agent-api/Cargo.toml index da1f2f29134..ce4d4863117 100644 --- a/sled-agent/bootstrap-agent-api/Cargo.toml +++ b/sled-agent/bootstrap-agent-api/Cargo.toml @@ -9,6 +9,7 @@ workspace = true [dependencies] dropshot.workspace = true +dropshot-api-manager-types.workspace = true omicron-common.workspace = true omicron-uuid-kinds.workspace = true omicron-workspace-hack.workspace = true diff --git a/sled-agent/bootstrap-agent-api/src/lib.rs b/sled-agent/bootstrap-agent-api/src/lib.rs index 88175b85775..845408b87ed 100644 --- a/sled-agent/bootstrap-agent-api/src/lib.rs +++ b/sled-agent/bootstrap-agent-api/src/lib.rs @@ -11,6 +11,7 @@ use dropshot::{ HttpError, HttpResponseOk, HttpResponseUpdatedNoContent, RequestContext, TypedBody, }; +use dropshot_api_manager_types::api_versions; use omicron_uuid_kinds::{RackInitUuid, RackResetUuid}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -20,6 +21,12 @@ use sled_agent_types::{ use sled_hardware_types::Baseboard; use tufaceous_artifact::ArtifactVersion; +api_versions!([ + // Do not create new versions of this client-side versioned API. + // https://github.com/oxidecomputer/omicron/issues/9290 + (1, INITIAL), +]); + #[dropshot::api_description] pub trait BootstrapAgentApi { type Context; diff --git a/sled-agent/repo-depot-api/Cargo.toml b/sled-agent/repo-depot-api/Cargo.toml index 335d301ab2f..c4a7ead2ee2 100644 --- a/sled-agent/repo-depot-api/Cargo.toml +++ b/sled-agent/repo-depot-api/Cargo.toml @@ -9,6 +9,7 @@ workspace = true [dependencies] dropshot.workspace = true +dropshot-api-manager-types.workspace = true omicron-workspace-hack.workspace = true schemars.workspace = true serde.workspace = true diff --git a/sled-agent/repo-depot-api/src/lib.rs b/sled-agent/repo-depot-api/src/lib.rs index dba0582cadb..1741f843e6e 100644 --- a/sled-agent/repo-depot-api/src/lib.rs +++ b/sled-agent/repo-depot-api/src/lib.rs @@ -3,10 +3,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use dropshot::{FreeformBody, HttpError, HttpResponseOk, Path, RequestContext}; +use dropshot_api_manager_types::api_versions; use schemars::JsonSchema; use serde::Deserialize; use tufaceous_artifact::ArtifactHash; +api_versions!([ + // Do not create new versions of this client-side versioned API. + // https://github.com/oxidecomputer/omicron/issues/9290 + (1, INITIAL), +]); + #[dropshot::api_description] pub trait RepoDepotApi { type Context; diff --git a/sled-agent/src/artifact_store.rs b/sled-agent/src/artifact_store.rs index deb1156568c..d3f782a8602 100644 --- a/sled-agent/src/artifact_store.rs +++ b/sled-agent/src/artifact_store.rs @@ -174,6 +174,12 @@ impl ArtifactStore { bind_address: depot_address.into(), ..dropshot_config.clone() }) + .version_policy(dropshot::VersionPolicy::Dynamic(Box::new( + dropshot::ClientSpecifiesVersionInHeader::new( + omicron_common::api::VERSION_HEADER, + repo_depot_api::latest_version(), + ), + ))) .start() .map_err(StartError::Dropshot) } diff --git a/sled-agent/src/bootstrap/server.rs b/sled-agent/src/bootstrap/server.rs index 6b9ca4b890d..78499f9e6dd 100644 --- a/sled-agent/src/bootstrap/server.rs +++ b/sled-agent/src/bootstrap/server.rs @@ -454,6 +454,12 @@ fn start_dropshot_server( dropshot_log, ) .config(dropshot_config) + .version_policy(dropshot::VersionPolicy::Dynamic(Box::new( + dropshot::ClientSpecifiesVersionInHeader::new( + omicron_common::api::VERSION_HEADER, + bootstrap_agent_api::latest_version(), + ), + ))) .start() .map_err(|error| { StartError::InitBootstrapDropshotServer(error.to_string()) diff --git a/sled-agent/src/sim/artifact_store.rs b/sled-agent/src/sim/artifact_store.rs index 00f6f475465..febc92fdd34 100644 --- a/sled-agent/src/sim/artifact_store.rs +++ b/sled-agent/src/sim/artifact_store.rs @@ -84,6 +84,12 @@ impl ArtifactStore { log.new(o!("component" => "dropshot (Repo Depot)")), ) .config(dropshot_config.clone()) + .version_policy(dropshot::VersionPolicy::Dynamic(Box::new( + dropshot::ClientSpecifiesVersionInHeader::new( + omicron_common::api::VERSION_HEADER, + repo_depot_api::latest_version(), + ), + ))) .start() .unwrap() } diff --git a/wicketd/src/lib.rs b/wicketd/src/lib.rs index c1a7da0b4f8..6f543b1033c 100644 --- a/wicketd/src/lib.rs +++ b/wicketd/src/lib.rs @@ -231,6 +231,12 @@ impl Server { log, ) .config(installinator_config) + .version_policy(dropshot::VersionPolicy::Dynamic(Box::new( + dropshot::ClientSpecifiesVersionInHeader::new( + omicron_common::api::VERSION_HEADER, + installinator_api::latest_version(), + ), + ))) .start() .map_err(|err| { anyhow!(err)