Skip to content

Commit

Permalink
[nexus] Support Bundles Datastore methods (#7021)
Browse files Browse the repository at this point in the history
PR 2 / ???

Adds Support Bundles to the database, along with datastore-specific
tests.

These implementations are necessary to implement both the Support Bundle
background task,
in #7063 , as well as the
HTTP interface for support
bundles, in #7187
  • Loading branch information
smklein authored Jan 6, 2025
1 parent 1200dcb commit 94d76b8
Show file tree
Hide file tree
Showing 12 changed files with 1,706 additions and 26 deletions.
2 changes: 2 additions & 0 deletions nexus/db-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ mod sled_state;
mod sled_underlay_subnet_allocation;
mod snapshot;
mod ssh_key;
mod support_bundle;
mod switch;
mod tuf_repo;
mod typed_uuid;
Expand Down Expand Up @@ -204,6 +205,7 @@ pub use sled_state::*;
pub use sled_underlay_subnet_allocation::*;
pub use snapshot::*;
pub use ssh_key::*;
pub use support_bundle::*;
pub use switch::*;
pub use switch_interface::*;
pub use switch_port::*;
Expand Down
15 changes: 15 additions & 0 deletions nexus/db-model/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,20 @@ joinable!(tuf_repo_artifact -> tuf_repo (tuf_repo_id));
// Can't specify joinable for a composite primary key (tuf_repo_artifact ->
// tuf_artifact).

table! {
support_bundle {
id -> Uuid,
time_created -> Timestamptz,
reason_for_creation -> Text,
reason_for_failure -> Nullable<Text>,
state -> crate::SupportBundleStateEnum,
zpool_id -> Uuid,
dataset_id -> Uuid,

assigned_nexus -> Nullable<Uuid>,
}
}

/* hardware inventory */

table! {
Expand Down Expand Up @@ -2034,6 +2048,7 @@ allow_tables_to_appear_in_same_query!(
console_session,
sled,
sled_resource,
support_bundle,
router_route,
vmm,
volume,
Expand Down
3 changes: 2 additions & 1 deletion nexus/db-model/src/schema_versions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use std::collections::BTreeMap;
///
/// This must be updated when you change the database schema. Refer to
/// schema/crdb/README.adoc in the root of this repository for details.
pub const SCHEMA_VERSION: SemverVersion = SemverVersion::new(117, 0, 0);
pub const SCHEMA_VERSION: SemverVersion = SemverVersion::new(118, 0, 0);

/// List of all past database schema versions, in *reverse* order
///
Expand All @@ -29,6 +29,7 @@ static KNOWN_VERSIONS: Lazy<Vec<KnownVersion>> = Lazy::new(|| {
// | leaving the first copy as an example for the next person.
// v
// KnownVersion::new(next_int, "unique-dirname-with-the-sql-files"),
KnownVersion::new(118, "support-bundles"),
KnownVersion::new(117, "add-completing-and-new-region-volume"),
KnownVersion::new(116, "bp-physical-disk-disposition"),
KnownVersion::new(115, "inv-omicron-physical-disks-generation"),
Expand Down
133 changes: 133 additions & 0 deletions nexus/db-model/src/support_bundle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use super::impl_enum_type;
use crate::schema::support_bundle;
use crate::typed_uuid::DbTypedUuid;

use chrono::{DateTime, Utc};
use nexus_types::external_api::shared::SupportBundleInfo as SupportBundleView;
use nexus_types::external_api::shared::SupportBundleState as SupportBundleStateView;
use omicron_uuid_kinds::DatasetKind;
use omicron_uuid_kinds::DatasetUuid;
use omicron_uuid_kinds::OmicronZoneKind;
use omicron_uuid_kinds::OmicronZoneUuid;
use omicron_uuid_kinds::SupportBundleKind;
use omicron_uuid_kinds::SupportBundleUuid;
use omicron_uuid_kinds::ZpoolKind;
use omicron_uuid_kinds::ZpoolUuid;
use serde::{Deserialize, Serialize};

impl_enum_type!(
#[derive(SqlType, Debug, QueryId)]
#[diesel(postgres_type(name = "support_bundle_state", schema = "public"))]
pub struct SupportBundleStateEnum;

#[derive(Copy, Clone, Debug, AsExpression, FromSqlRow, Serialize, Deserialize, PartialEq)]
#[diesel(sql_type = SupportBundleStateEnum)]
pub enum SupportBundleState;

// Enum values
Collecting => b"collecting"
Active => b"active"
Destroying => b"destroying"
Failing => b"failing"
Failed => b"failed"
);

impl SupportBundleState {
/// Returns the list of valid prior states.
///
/// This is used to confirm that state updates are performed legally,
/// and defines the possible state transitions.
pub fn valid_old_states(&self) -> Vec<SupportBundleState> {
use SupportBundleState::*;

match self {
Collecting => vec![],
Active => vec![Collecting],
// The "Destroying" state is terminal.
Destroying => vec![Active, Collecting, Failing],
Failing => vec![Collecting, Active],
// The "Failed" state is terminal.
Failed => vec![Active, Collecting, Failing],
}
}
}

impl From<SupportBundleState> for SupportBundleStateView {
fn from(state: SupportBundleState) -> Self {
use SupportBundleState::*;

match state {
Collecting => SupportBundleStateView::Collecting,
Active => SupportBundleStateView::Active,
Destroying => SupportBundleStateView::Destroying,
// The distinction between "failing" and "failed" should not be
// visible to end-users. This is internal book-keeping to decide
// whether or not the bundle record can be safely deleted.
//
// Either way, it should be possible to delete the bundle.
// If a user requests that we delete a bundle in these states:
// - "Failing" bundles will become "Destroying"
// - "Failed" bundles can be deleted immediately
Failing => SupportBundleStateView::Failed,
Failed => SupportBundleStateView::Failed,
}
}
}

#[derive(
Queryable,
Insertable,
Debug,
Clone,
Selectable,
Deserialize,
Serialize,
PartialEq,
)]
#[diesel(table_name = support_bundle)]
pub struct SupportBundle {
pub id: DbTypedUuid<SupportBundleKind>,
pub time_created: DateTime<Utc>,
pub reason_for_creation: String,
pub reason_for_failure: Option<String>,
pub state: SupportBundleState,
pub zpool_id: DbTypedUuid<ZpoolKind>,
pub dataset_id: DbTypedUuid<DatasetKind>,
pub assigned_nexus: Option<DbTypedUuid<OmicronZoneKind>>,
}

impl SupportBundle {
pub fn new(
reason_for_creation: &'static str,
zpool_id: ZpoolUuid,
dataset_id: DatasetUuid,
nexus_id: OmicronZoneUuid,
) -> Self {
Self {
id: SupportBundleUuid::new_v4().into(),
time_created: Utc::now(),
reason_for_creation: reason_for_creation.to_string(),
reason_for_failure: None,
state: SupportBundleState::Collecting,
zpool_id: zpool_id.into(),
dataset_id: dataset_id.into(),
assigned_nexus: Some(nexus_id.into()),
}
}
}

impl From<SupportBundle> for SupportBundleView {
fn from(bundle: SupportBundle) -> Self {
Self {
id: bundle.id.into(),
time_created: bundle.time_created,
reason_for_creation: bundle.reason_for_creation,
reason_for_failure: bundle.reason_for_failure,
state: bundle.state.into(),
}
}
}
25 changes: 1 addition & 24 deletions nexus/db-queries/src/db/datastore/dataset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,14 +352,13 @@ impl DataStore {
#[cfg(test)]
mod test {
use super::*;
use crate::db::datastore::test::bp_insert_and_make_target;
use crate::db::pub_test_utils::TestDatabase;
use nexus_db_model::Generation;
use nexus_db_model::SledBaseboard;
use nexus_db_model::SledSystemHardware;
use nexus_db_model::SledUpdate;
use nexus_reconfigurator_planning::blueprint_builder::BlueprintBuilder;
use nexus_types::deployment::Blueprint;
use nexus_types::deployment::BlueprintTarget;
use omicron_common::api::internal::shared::DatasetKind as ApiDatasetKind;
use omicron_test_utils::dev;
use omicron_uuid_kinds::DatasetUuid;
Expand Down Expand Up @@ -523,28 +522,6 @@ mod test {
logctx.cleanup_successful();
}

async fn bp_insert_and_make_target(
opctx: &OpContext,
datastore: &DataStore,
bp: &Blueprint,
) {
datastore
.blueprint_insert(opctx, bp)
.await
.expect("inserted blueprint");
datastore
.blueprint_target_set_current(
opctx,
BlueprintTarget {
target_id: bp.id,
enabled: true,
time_made_target: Utc::now(),
},
)
.await
.expect("made blueprint the target");
}

fn new_dataset_on(zpool_id: ZpoolUuid) -> Dataset {
Dataset::new(
DatasetUuid::new_v4(),
Expand Down
30 changes: 30 additions & 0 deletions nexus/db-queries/src/db/datastore/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ mod sled;
mod sled_instance;
mod snapshot;
mod ssh_key;
mod support_bundle;
mod switch;
mod switch_interface;
mod switch_port;
Expand Down Expand Up @@ -464,6 +465,8 @@ mod test {
use nexus_db_fixed_data::silo::DEFAULT_SILO;
use nexus_db_model::IpAttachState;
use nexus_db_model::{to_db_typed_uuid, Generation};
use nexus_types::deployment::Blueprint;
use nexus_types::deployment::BlueprintTarget;
use nexus_types::external_api::params;
use nexus_types::silo::DEFAULT_SILO_ID;
use omicron_common::api::external::{
Expand Down Expand Up @@ -504,6 +507,33 @@ mod test {
}
}

/// Inserts a blueprint in the DB and forcibly makes it the target
///
/// WARNING: This makes no attempts to validate the blueprint relative to
/// parents -- this is just a test-only helper to make testing
/// blueprint-specific checks easier.
pub async fn bp_insert_and_make_target(
opctx: &OpContext,
datastore: &DataStore,
bp: &Blueprint,
) {
datastore
.blueprint_insert(opctx, bp)
.await
.expect("inserted blueprint");
datastore
.blueprint_target_set_current(
opctx,
BlueprintTarget {
target_id: bp.id,
enabled: true,
time_made_target: Utc::now(),
},
)
.await
.expect("made blueprint the target");
}

#[tokio::test]
async fn test_project_creation() {
let logctx = dev::test_setup_log("test_project_creation");
Expand Down
Loading

0 comments on commit 94d76b8

Please sign in to comment.