Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion common/src/api/external/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3571,7 +3571,7 @@ pub enum ImportExportPolicy {
/// will fail to parse if the key is not present. The JSON Schema in the
/// OpenAPI definition will also reflect that the field is required. See
/// <https://github.com/serde-rs/serde/issues/2753>.
#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
pub struct Nullable<T>(pub Option<T>);

impl<T> From<Option<T>> for Nullable<T> {
Expand Down
12 changes: 0 additions & 12 deletions nexus/db-model/src/target_release.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use super::{Generation, impl_enum_type};
use crate::typed_uuid::DbTypedUuid;
use chrono::{DateTime, Utc};
use nexus_db_schema::schema::target_release;
use nexus_types::external_api::views;
use omicron_uuid_kinds::TufRepoKind;

impl_enum_type!(
Expand Down Expand Up @@ -60,15 +59,4 @@ impl TargetRelease {
tuf_repo_id: Some(tuf_repo_id),
}
}

pub fn into_external(
&self,
release_source: views::TargetReleaseSource,
) -> views::TargetRelease {
views::TargetRelease {
generation: (&self.generation.0).into(),
time_requested: self.time_requested,
release_source,
}
}
}
48 changes: 1 addition & 47 deletions nexus/db-queries/src/db/datastore/target_release.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,14 @@
use super::DataStore;
use crate::authz;
use crate::context::OpContext;
use crate::db::model::{
Generation, SemverVersion, TargetRelease, TargetReleaseSource,
};
use crate::db::model::{Generation, TargetRelease};
use async_bb8_diesel::AsyncRunQueryDsl as _;
use diesel::insert_into;
use diesel::prelude::*;
use diesel::sql_types;
use nexus_db_errors::{ErrorHandler, public_error_from_diesel};
use nexus_db_schema::enums::TargetReleaseSourceEnum;
use nexus_db_schema::schema::target_release::dsl;
use nexus_types::external_api::views;
use omicron_common::api::external::{CreateResult, Error, LookupResult};
use omicron_uuid_kinds::TufRepoUuid;
use std::collections::BTreeSet;
Expand Down Expand Up @@ -181,49 +178,6 @@ impl DataStore {
}
}

/// Convert a model-level target release to an external view.
/// This method lives here because we have to look up the version
/// corresponding to the TUF repo.
pub async fn target_release_view(
&self,
opctx: &OpContext,
target_release: &TargetRelease,
) -> LookupResult<views::TargetRelease> {
opctx
.authorize(authz::Action::Read, &authz::TARGET_RELEASE_CONFIG)
.await?;
let conn = self.pool_connection_authorized(opctx).await?;
let release_source = match target_release.release_source {
TargetReleaseSource::Unspecified => {
views::TargetReleaseSource::Unspecified
}
TargetReleaseSource::SystemVersion => {
use nexus_db_schema::schema::tuf_repo;
if let Some(tuf_repo_id) = target_release.tuf_repo_id {
views::TargetReleaseSource::SystemVersion {
version: tuf_repo::table
.select(tuf_repo::system_version)
.filter(tuf_repo::id.eq(tuf_repo_id))
.first_async::<SemverVersion>(&*conn)
.await
.map_err(|e| {
public_error_from_diesel(
e,
ErrorHandler::Server,
)
})?
.into(),
}
} else {
return Err(Error::internal_error(
"missing TUF repo ID for specified system version",
));
}
}
};
Ok(target_release.into_external(release_source))
}

/// Lists the most recent N distinct target releases
pub async fn target_release_fetch_recent_distinct(
&self,
Expand Down
31 changes: 29 additions & 2 deletions nexus/db-queries/src/db/datastore/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ use nexus_db_errors::OptionalError;
use nexus_db_errors::{ErrorHandler, public_error_from_diesel};
use nexus_db_lookup::DbConnection;
use nexus_db_model::{
ArtifactHash, TargetRelease, TufArtifact, TufRepo, TufRepoDescription,
TufTrustRoot, to_db_typed_uuid,
ArtifactHash, DbTypedUuid, TargetRelease, TufArtifact, TufRepo,
TufRepoDescription, TufTrustRoot, to_db_typed_uuid,
};
use omicron_common::api::external::{
self, CreateResult, DataPageParams, DeleteResult, Generation,
Expand Down Expand Up @@ -180,6 +180,33 @@ impl DataStore {
Ok(TufRepoDescription { repo, artifacts })
}

/// Given a TUF repo ID, get its version. We could use `tuf_repo_get_by_id`,
/// but that makes an additional query for the artifacts that we don't need
/// in the code that uses this method.
pub async fn tuf_repo_get_version(
&self,
opctx: &OpContext,
tuf_repo_id: &DbTypedUuid<TufRepoKind>,
) -> LookupResult<semver::Version> {
opctx
.authorize(authz::Action::Read, &authz::TARGET_RELEASE_CONFIG)
.await?;
let conn = self.pool_connection_authorized(opctx).await?;
use nexus_db_schema::schema::tuf_repo;
tuf_repo::table
.select(tuf_repo::system_version)
.filter(tuf_repo::id.eq(tuf_repo_id.into_untyped_uuid()))
.first_async::<SemverVersion>(&*conn)
.await
.map(|v| v.0)
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server))
// looking up a non-existent ID will 500, but it doesn't
// automatically include the bad ID
.with_internal_context(|| {
format!("tuf_repo_get_version {tuf_repo_id}")
})
}

/// Returns the list of all TUF repo artifacts known to the system.
pub async fn tuf_list_repos(
&self,
Expand Down
2 changes: 1 addition & 1 deletion nexus/external-api/output/nexus_tags.txt
Original file line number Diff line number Diff line change
Expand Up @@ -298,12 +298,12 @@ API operations found with tag "system/update"
OPERATION ID METHOD URL PATH
system_update_get_repository GET /v1/system/update/repository/{system_version}
system_update_put_repository PUT /v1/system/update/repository
system_update_status GET /v1/system/update/status
system_update_trust_root_create POST /v1/system/update/trust-roots
system_update_trust_root_delete DELETE /v1/system/update/trust-roots/{trust_root_id}
system_update_trust_root_list GET /v1/system/update/trust-roots
system_update_trust_root_view GET /v1/system/update/trust-roots/{trust_root_id}
target_release_update PUT /v1/system/update/target-release
target_release_view GET /v1/system/update/target-release

API operations found with tag "tokens"
OPERATION ID METHOD URL PATH
Expand Down
35 changes: 17 additions & 18 deletions nexus/external-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3041,36 +3041,35 @@ pub trait NexusExternalApi {
path_params: Path<params::TufTrustRootPath>,
) -> Result<HttpResponseDeleted, HttpError>;

/// Get the current target release of the rack's system software
/// Set target release
///
/// This may not correspond to the actual software running on the rack
/// at the time of request; it is instead the release that the rack
/// reconfigurator should be moving towards as a goal state. After some
/// number of planning and execution phases, the software running on the
/// rack should eventually correspond to the release described here.
/// Set the current target release of the rack's system software. The rack
/// reconfigurator will treat the software specified here as a goal state
/// for the rack's software, and attempt to asynchronously update to that
/// release. Use the update status endpoint to view the current target
/// release.
#[endpoint {
method = GET,
method = PUT,
path = "/v1/system/update/target-release",
tags = ["system/update"],
}]
async fn target_release_view(
async fn target_release_update(
rqctx: RequestContext<Self::Context>,
) -> Result<HttpResponseOk<views::TargetRelease>, HttpError>;
params: TypedBody<params::SetTargetReleaseParams>,
) -> Result<HttpResponseUpdatedNoContent, HttpError>;

/// Set the current target release of the rack's system software
/// Fetch system update status
///
/// The rack reconfigurator will treat the software specified here as
/// a goal state for the rack's software, and attempt to asynchronously
/// update to that release.
/// Returns information about the current target release and the
/// progress of system software updates.
#[endpoint {
method = PUT,
path = "/v1/system/update/target-release",
method = GET,
path = "/v1/system/update/status",
tags = ["system/update"],
}]
async fn target_release_update(
async fn system_update_status(
rqctx: RequestContext<Self::Context>,
params: TypedBody<params::SetTargetReleaseParams>,
) -> Result<HttpResponseCreated<views::TargetRelease>, HttpError>;
) -> Result<HttpResponseOk<views::UpdateStatus>, HttpError>;

// Silo users

Expand Down
7 changes: 6 additions & 1 deletion nexus/src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use self::saga::SagaExecutor;
use crate::DropshotServer;
use crate::app::background::BackgroundTasksData;
use crate::app::background::SagaRecoveryHelpers;
use crate::app::update::UpdateStatusHandle;
use crate::populate::PopulateArgs;
use crate::populate::PopulateStatus;
use crate::populate::populate_start;
Expand Down Expand Up @@ -285,6 +286,9 @@ pub struct Nexus {
#[allow(dead_code)]
repo_depot_resolver: Box<dyn qorb::resolver::Resolver>,

/// handle to pull update status data
update_status: UpdateStatusHandle,

/// state of overall Nexus quiesce activity
quiesce: NexusQuiesceHandle,
}
Expand Down Expand Up @@ -351,7 +355,7 @@ impl Nexus {
let quiesce = NexusQuiesceHandle::new(
db_datastore.clone(),
config.deployment.id,
blueprint_load_rx,
blueprint_load_rx.clone(),
quiesce_opctx,
);

Expand Down Expand Up @@ -534,6 +538,7 @@ impl Nexus {
mgs_update_status_rx,
mgs_resolver,
repo_depot_resolver,
update_status: UpdateStatusHandle::new(blueprint_load_rx),
quiesce,
};

Expand Down
Loading
Loading