Skip to content

Commit d0ef0ce

Browse files
committed
so damn close with the component join table but it doesn't compile
1 parent cde4429 commit d0ef0ce

File tree

7 files changed

+187
-14
lines changed

7 files changed

+187
-14
lines changed

common/src/sql/dbinit.sql

+42
Original file line numberDiff line numberDiff line change
@@ -1433,6 +1433,48 @@ CREATE TABLE omicron.public.system_update (
14331433
version STRING(40) NOT NULL
14341434
);
14351435

1436+
-- TODO: wtf are the values here
1437+
CREATE TYPE omicron.public.device_type AS ENUM (
1438+
'disk'
1439+
);
1440+
1441+
/*
1442+
* Component updates. Associated with at least one system_update through
1443+
* system_update_component_update.
1444+
*/
1445+
CREATE TABLE omicron.public.component_update (
1446+
/* Identity metadata (asset) */
1447+
id UUID PRIMARY KEY,
1448+
time_created TIMESTAMPTZ NOT NULL,
1449+
time_modified TIMESTAMPTZ NOT NULL,
1450+
1451+
-- On component updates there's no device ID because the update can apply to
1452+
-- multiple instances of a given device kind
1453+
1454+
version STRING(40) NOT NULL,
1455+
device_type omicron.public.device_type NOT NULL,
1456+
parent_id UUID
1457+
);
1458+
1459+
/*
1460+
* Associate system updates with component updates. Not done with a
1461+
* system_update_id field on component_update because the same component update
1462+
* may be part of more than one system update.
1463+
*
1464+
* TODO: is the one-to-many association necessary? Things would be simpler if we
1465+
* created a new component_update for every (system update, component update).
1466+
* Downsides: less flexibility, possibly a _lot_ of duplication, depending how
1467+
* often component updates are actually unique. I suspect many system updates
1468+
* will only change a couple of components, which means most component updates
1469+
* will appear in more than one system update.
1470+
*/
1471+
CREATE TABLE omicron.public.system_update_component_update (
1472+
system_update_id UUID NOT NULL,
1473+
component_update_id UUID NOT NULL,
1474+
1475+
PRIMARY KEY (system_update_id, component_update_id)
1476+
);
1477+
14361478
/*******************************************************************/
14371479

14381480
/*

nexus/db-model/src/schema.rs

+29
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,35 @@ table! {
645645
}
646646
}
647647

648+
table! {
649+
component_update (id) {
650+
id -> Uuid,
651+
name -> Text,
652+
description -> Text,
653+
time_created -> Timestamptz,
654+
time_modified -> Timestamptz,
655+
656+
version -> Text,
657+
device_type -> crate::DeviceTypeEnum,
658+
// parent component update ID
659+
// TODO: spell out the whole horrible name?
660+
parent_id -> Uuid,
661+
}
662+
}
663+
664+
table! {
665+
system_update_component_update (system_update_id, component_update_id) {
666+
system_update_id -> Uuid,
667+
component_update_id -> Uuid,
668+
}
669+
}
670+
671+
allow_tables_to_appear_in_same_query!(
672+
system_update,
673+
component_update,
674+
system_update_component_update,
675+
);
676+
648677
allow_tables_to_appear_in_same_query!(ip_pool_range, ip_pool);
649678
joinable!(ip_pool_range -> ip_pool (ip_pool_id));
650679

nexus/db-model/src/system_update.rs

+60-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5-
use crate::schema::system_update;
5+
use crate::{
6+
impl_enum_type,
7+
schema::{component_update, system_update},
8+
};
69
use db_macros::Asset;
710
use nexus_types::{external_api::views, identity::Asset};
811
use serde::{Deserialize, Serialize};
12+
use uuid::Uuid;
913

1014
#[derive(
1115
Queryable,
@@ -34,3 +38,58 @@ impl From<SystemUpdate> for views::SystemUpdate {
3438
}
3539
}
3640
}
41+
42+
// TODO: more specific name than device_type, maybe update_device_type
43+
44+
impl_enum_type!(
45+
#[derive(SqlType, Debug, QueryId)]
46+
#[diesel(postgres_type(name = "device_type"))]
47+
pub struct DeviceTypeEnum;
48+
49+
#[derive(Copy, Clone, Debug, AsExpression, FromSqlRow, Serialize, Deserialize, PartialEq)]
50+
#[diesel(sql_type = DeviceTypeEnum)]
51+
pub enum DeviceType;
52+
53+
// Enum values
54+
Disk => b"disk"
55+
);
56+
57+
impl From<DeviceType> for views::DeviceType {
58+
fn from(device_type: DeviceType) -> Self {
59+
match device_type {
60+
DeviceType::Disk => views::DeviceType::Disk,
61+
}
62+
}
63+
}
64+
65+
#[derive(
66+
Queryable,
67+
Insertable,
68+
Selectable,
69+
Clone,
70+
Debug,
71+
Asset,
72+
Serialize,
73+
Deserialize,
74+
)]
75+
#[diesel(table_name = component_update)]
76+
pub struct ComponentUpdate {
77+
#[diesel(embed)]
78+
identity: ComponentUpdateIdentity,
79+
pub version: String,
80+
pub device_type: DeviceType,
81+
pub parent_id: Option<Uuid>,
82+
}
83+
84+
impl From<ComponentUpdate> for views::ComponentUpdate {
85+
fn from(component_update: ComponentUpdate) -> Self {
86+
Self {
87+
identity: component_update.identity(),
88+
// TODO: figure out how to ser/de semver versions
89+
// version: system_update.version,
90+
version: views::SemverVersion::new(1, 0, 0),
91+
device_type: component_update.device_type.into(),
92+
parent_id: component_update.parent_id,
93+
}
94+
}
95+
}

nexus/src/app/update.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::db::lookup::LookupPath;
1212
use crate::db::model::UpdateArtifactKind;
1313
use hex;
1414
use omicron_common::api::external::{
15-
DataPageParams, Error, LookupResult, PaginationOrder,
15+
DataPageParams, Error, ListResultVec, LookupResult, PaginationOrder,
1616
};
1717
use omicron_common::api::internal::nexus::UpdateArtifact;
1818
use rand::Rng;
@@ -290,4 +290,17 @@ impl super::Nexus {
290290
.await?;
291291
Ok(db_system_update)
292292
}
293+
294+
pub async fn system_update_list_components(
295+
&self,
296+
opctx: &OpContext,
297+
update_id: &Uuid,
298+
) -> ListResultVec<db::model::ComponentUpdate> {
299+
let (authz_update, ..) = LookupPath::new(opctx, &self.db_datastore)
300+
.system_update_id(*update_id)
301+
.fetch()
302+
.await?;
303+
304+
self.db_datastore.components_list(opctx, &authz_update).await
305+
}
293306
}

nexus/src/db/datastore/update.rs

+31-4
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ use crate::context::OpContext;
1010
use crate::db;
1111
use crate::db::error::public_error_from_diesel_pool;
1212
use crate::db::error::ErrorHandler;
13-
use crate::db::model::UpdateAvailableArtifact;
13+
use crate::db::model::{ComponentUpdate, UpdateAvailableArtifact};
1414
use async_bb8_diesel::AsyncRunQueryDsl;
1515
use diesel::prelude::*;
16-
use omicron_common::api::external::CreateResult;
17-
use omicron_common::api::external::DeleteResult;
18-
use omicron_common::api::external::InternalContext;
16+
use omicron_common::api::external::{
17+
CreateResult, DeleteResult, InternalContext, ListResultVec,
18+
};
1919

2020
impl DataStore {
2121
pub async fn update_available_artifact_upsert(
@@ -56,4 +56,31 @@ impl DataStore {
5656
.map_err(|e| public_error_from_diesel_pool(e, ErrorHandler::Server))
5757
.internal_context("deleting outdated available artifacts")
5858
}
59+
60+
pub async fn components_list(
61+
&self,
62+
opctx: &OpContext,
63+
authz_update: &authz::SystemUpdate,
64+
) -> ListResultVec<ComponentUpdate> {
65+
// TODO: what's the right permission here?
66+
opctx.authorize(authz::Action::Read, &authz::FLEET).await?;
67+
68+
use db::schema::component_update::dsl as component_update;
69+
use db::schema::system_update_component_update::dsl as join_table;
70+
71+
component_update::component_update
72+
.inner_join(
73+
join_table::system_update_component_update.on(
74+
join_table::component_update_id
75+
.eq(component_update::id)
76+
.and(
77+
join_table::system_update_id.eq(authz_update.id()),
78+
),
79+
),
80+
)
81+
.select(ComponentUpdate::as_select())
82+
.load_async(self.pool_authorized(opctx).await?)
83+
.await
84+
.map_err(|e| public_error_from_diesel_pool(e, ErrorHandler::Server))
85+
}
5986
}

nexus/src/external_api/http_entrypoints.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -5165,9 +5165,13 @@ async fn system_update_components_list(
51655165
let path = path_params.into_inner();
51665166
let handler = async {
51675167
let opctx = OpContext::for_external_api(&rqctx).await?;
5168-
let _system_update =
5169-
nexus.system_update_fetch_by_id(&opctx, &path.update_id).await?;
5170-
Ok(HttpResponseOk(ResultsPage { items: vec![], next_page: None }))
5168+
let components = nexus
5169+
.system_update_list_components(&opctx, &path.update_id)
5170+
.await?
5171+
.into_iter()
5172+
.map(|i| i.into())
5173+
.collect();
5174+
Ok(HttpResponseOk(ResultsPage { items: components, next_page: None }))
51715175
};
51725176
apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await
51735177
}

nexus/types/src/external_api/views.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -466,16 +466,15 @@ pub struct SystemUpdate {
466466
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
467467
#[serde(rename_all = "snake_case")]
468468
pub enum DeviceType {
469-
Sled,
469+
Disk,
470470
}
471471

472472
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
473473
pub struct ComponentUpdate {
474-
// note this is the ID of the component, not the update. Does the component
475-
// update also have an ID?
476-
pub component_id: Uuid,
474+
#[serde(flatten)]
475+
pub identity: AssetIdentityMetadata,
476+
477477
// pub name: Name, // hmmm. who would name this?
478-
pub device_id: String,
479478
pub device_type: DeviceType,
480479
pub version: SemverVersion,
481480
// TODO: parent ID doesn't need to be optional if we say top-level

0 commit comments

Comments
 (0)