Skip to content

Commit 529ba64

Browse files
authored
[nexus] Add TUF repo list endpoint, make other endpoints conventional (#9106)
Built on top of #8897 These are breaking API changes and I won't merge until I have a PR up on the CLI side that makes this all work. ### Add list endpoint and fix other ones Made the endpoints match the other ones. Not married to the verb `upload`, but `system_update_repository_update` felt a little silly. ```diff -system_update_get_repository GET /v1/system/update/repository/{system_version} -system_update_put_repository PUT /v1/system/update/repository +system_update_repository_list GET /v1/system/update/repositories +system_update_repository_upload PUT /v1/system/update/repositories +system_update_repository_view GET /v1/system/update/repositories/{system_version} ``` ### Standardize and simplify response types What's responsible for this PR being so big is that I reworked these endpoints to return a new `TufRepo` struct that is only the metadata and no artifacts. The main reason for this was that the list endpoint would have to pull artifacts for every repo in the list. The list is likely to be small, so it's probably not a big deal, but it also seems that the artifacts are not useful to the end user. Once the list endpoint was simplified, it makes sense to return the same thing from the view and update endpoints. The upload endpoint returns a `TufRepoUpload` which has the `TufRepo` together with an indicator saying whether the repo contents were new repo or already existed.
1 parent 8c683c7 commit 529ba64

File tree

17 files changed

+719
-421
lines changed

17 files changed

+719
-421
lines changed

Cargo.lock

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

common/src/api/external/http_pagination.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ use dropshot::RequestContext;
5353
use dropshot::ResultsPage;
5454
use dropshot::WhichPage;
5555
use schemars::JsonSchema;
56+
use semver::Version;
5657
use serde::Deserialize;
5758
use serde::Serialize;
5859
use serde::de::DeserializeOwned;
@@ -163,6 +164,54 @@ pub fn marker_for_name_or_id<T: SimpleIdentityOrName, Selector>(
163164
}
164165
}
165166

167+
// Pagination by semantic version in ascending or descending order
168+
169+
/// Scan parameters for resources that support scanning by semantic version
170+
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
171+
pub struct ScanByVersion<Selector = ()> {
172+
#[serde(default = "default_version_sort_mode")]
173+
sort_by: VersionSortMode,
174+
#[serde(flatten)]
175+
pub selector: Selector,
176+
}
177+
178+
/// Supported sort modes when scanning by semantic version
179+
#[derive(Copy, Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
180+
#[serde(rename_all = "snake_case")]
181+
pub enum VersionSortMode {
182+
/// Sort in increasing semantic version order (oldest first)
183+
VersionAscending,
184+
/// Sort in decreasing semantic version order (newest first)
185+
VersionDescending,
186+
}
187+
188+
fn default_version_sort_mode() -> VersionSortMode {
189+
VersionSortMode::VersionDescending
190+
}
191+
192+
impl<T> ScanParams for ScanByVersion<T>
193+
where
194+
T: Clone + Debug + DeserializeOwned + JsonSchema + PartialEq + Serialize,
195+
{
196+
type MarkerValue = Version;
197+
198+
fn direction(&self) -> PaginationOrder {
199+
match self.sort_by {
200+
VersionSortMode::VersionAscending => PaginationOrder::Ascending,
201+
VersionSortMode::VersionDescending => PaginationOrder::Descending,
202+
}
203+
}
204+
205+
fn from_query(
206+
p: &PaginationParams<Self, PageSelector<Self, Self::MarkerValue>>,
207+
) -> Result<&Self, HttpError> {
208+
Ok(match p.page {
209+
WhichPage::First(ref scan_params) => scan_params,
210+
WhichPage::Next(PageSelector { ref scan, .. }) => scan,
211+
})
212+
}
213+
}
214+
166215
/// See `dropshot::ResultsPage::new`
167216
fn page_selector_for<F, T, S, M>(
168217
item: &T,
@@ -313,6 +362,13 @@ pub type PaginatedByNameOrId<Selector = ()> = PaginationParams<
313362
pub type PageSelectorByNameOrId<Selector = ()> =
314363
PageSelector<ScanByNameOrId<Selector>, NameOrId>;
315364

365+
/// Query parameters for pagination by semantic version
366+
pub type PaginatedByVersion<Selector = ()> =
367+
PaginationParams<ScanByVersion<Selector>, PageSelectorByVersion<Selector>>;
368+
/// Page selector for pagination by semantic version
369+
pub type PageSelectorByVersion<Selector = ()> =
370+
PageSelector<ScanByVersion<Selector>, Version>;
371+
316372
pub fn id_pagination<'a, Selector>(
317373
pag_params: &'a DataPageParams<Uuid>,
318374
scan_params: &'a ScanById<Selector>,

common/src/api/external/mod.rs

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3427,6 +3427,10 @@ pub struct ServiceIcmpConfig {
34273427
pub enabled: bool,
34283428
}
34293429

3430+
// TODO: move these TUF repo structs out of this file. They're not external
3431+
// anymore after refactors that use views::TufRepo in the external API. They are
3432+
// still used extensively in internal services.
3433+
34303434
/// A description of an uploaded TUF repository.
34313435
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, JsonSchema)]
34323436
pub struct TufRepoDescription {
@@ -3501,40 +3505,6 @@ pub struct TufArtifactMeta {
35013505
pub sign: Option<Vec<u8>>,
35023506
}
35033507

3504-
/// Data about a successful TUF repo import into Nexus.
3505-
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
3506-
#[serde(rename_all = "snake_case")]
3507-
pub struct TufRepoInsertResponse {
3508-
/// The repository as present in the database.
3509-
pub recorded: TufRepoDescription,
3510-
3511-
/// Whether this repository already existed or is new.
3512-
pub status: TufRepoInsertStatus,
3513-
}
3514-
3515-
/// Status of a TUF repo import.
3516-
///
3517-
/// Part of `TufRepoInsertResponse`.
3518-
#[derive(
3519-
Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, JsonSchema,
3520-
)]
3521-
#[serde(rename_all = "snake_case")]
3522-
pub enum TufRepoInsertStatus {
3523-
/// The repository already existed in the database.
3524-
AlreadyExists,
3525-
3526-
/// The repository did not exist, and was inserted into the database.
3527-
Inserted,
3528-
}
3529-
3530-
/// Data about a successful TUF repo get from Nexus.
3531-
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
3532-
#[serde(rename_all = "snake_case")]
3533-
pub struct TufRepoGetResponse {
3534-
/// The description of the repository.
3535-
pub description: TufRepoDescription,
3536-
}
3537-
35383508
#[derive(
35393509
Clone, Debug, Deserialize, JsonSchema, Serialize, PartialEq, ObjectIdentity,
35403510
)]

nexus/db-model/src/tuf_repo.rs

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use nexus_db_schema::schema::{
1212
tuf_artifact, tuf_repo, tuf_repo_artifact, tuf_trust_root,
1313
};
1414
use nexus_types::external_api::shared::TufSignedRootRole;
15-
use nexus_types::external_api::views;
15+
use nexus_types::external_api::views::{self, TufRepoUploadStatus};
1616
use omicron_common::{api::external, update::ArtifactId};
1717
use omicron_uuid_kinds::GenericUuid;
1818
use omicron_uuid_kinds::TufArtifactKind;
@@ -30,8 +30,6 @@ use uuid::Uuid;
3030

3131
/// A description of a TUF update: a repo, along with the artifacts it
3232
/// contains.
33-
///
34-
/// This is the internal variant of [`external::TufRepoDescription`].
3533
#[derive(Debug, Clone)]
3634
pub struct TufRepoDescription {
3735
/// The repository.
@@ -64,7 +62,6 @@ impl TufRepoDescription {
6462
}
6563
}
6664

67-
/// Converts self into [`external::TufRepoDescription`].
6865
pub fn into_external(self) -> external::TufRepoDescription {
6966
external::TufRepoDescription {
7067
repo: self.repo.into_external(),
@@ -78,8 +75,6 @@ impl TufRepoDescription {
7875
}
7976

8077
/// A record representing an uploaded TUF repository.
81-
///
82-
/// This is the internal variant of [`external::TufRepoMeta`].
8378
#[derive(
8479
Queryable, Identifiable, Insertable, Clone, Debug, Selectable, AsChangeset,
8580
)]
@@ -134,7 +129,6 @@ impl TufRepo {
134129
)
135130
}
136131

137-
/// Converts self into [`external::TufRepoMeta`].
138132
pub fn into_external(self) -> external::TufRepoMeta {
139133
external::TufRepoMeta {
140134
hash: self.sha256.into(),
@@ -156,6 +150,17 @@ impl TufRepo {
156150
}
157151
}
158152

153+
impl From<TufRepo> for views::TufRepo {
154+
fn from(repo: TufRepo) -> views::TufRepo {
155+
views::TufRepo {
156+
hash: repo.sha256.into(),
157+
system_version: repo.system_version.into(),
158+
file_name: repo.file_name,
159+
time_created: repo.time_created,
160+
}
161+
}
162+
}
163+
159164
#[derive(Queryable, Insertable, Clone, Debug, Selectable, AsChangeset)]
160165
#[diesel(table_name = tuf_artifact)]
161166
pub struct TufArtifact {
@@ -413,3 +418,24 @@ impl FromSql<Jsonb, diesel::pg::Pg> for DbTufSignedRootRole {
413418
.map_err(|e| e.into())
414419
}
415420
}
421+
422+
// The following isn't a real model in the sense that it represents DB data,
423+
// but it is the return type of a datastore function. The main reason we can't
424+
// just use the view for this like we do with TufRepoUploadStatus is that
425+
// TufRepoDescription has a bit more info in it that we rely on in code outside
426+
// of the external API, like tests and internal APIs
427+
428+
/// The return value of the tuf repo insert function
429+
pub struct TufRepoUpload {
430+
pub recorded: TufRepoDescription,
431+
pub status: TufRepoUploadStatus,
432+
}
433+
434+
impl From<TufRepoUpload> for views::TufRepoUpload {
435+
fn from(upload: TufRepoUpload) -> Self {
436+
views::TufRepoUpload {
437+
repo: upload.recorded.repo.into(),
438+
status: upload.status,
439+
}
440+
}
441+
}

nexus/db-queries/src/db/datastore/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ mod switch_port;
111111
mod target_release;
112112
#[cfg(test)]
113113
pub(crate) mod test_utils;
114-
mod update;
114+
pub mod update;
115115
mod user_data_export;
116116
mod utilization;
117117
mod v2p_mapping;

0 commit comments

Comments
 (0)