Skip to content

Commit 5e835f1

Browse files
authored
nexus shattering 3 of 3: Extract nexus-db-model crate from nexus::db::model (#1478)
1 parent 4f86901 commit 5e835f1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+524
-408
lines changed

Cargo.lock

+27
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ members = [
1313
"nexus",
1414
"nexus/authz-macros",
1515
"nexus/db-macros",
16+
"nexus/db-model",
1617
"nexus/defaults",
1718
"nexus/test-utils",
1819
"nexus/test-utils-macros",
@@ -47,6 +48,7 @@ default-members = [
4748
"nexus",
4849
"nexus/authz-macros",
4950
"nexus/db-macros",
51+
"nexus/db-model",
5052
"nexus/defaults",
5153
"nexus/types",
5254
"package",

nexus/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ usdt = "0.3.1"
6161
authz-macros = { path = "authz-macros" }
6262
db-macros = { path = "db-macros" }
6363
nexus-defaults = { path = "defaults" }
64+
nexus-db-model = { path = "db-model" }
6465
nexus-types = { path = "types" }
6566

6667
[dependencies.chrono]

nexus/db-macros/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ fn build_resource_impl(
334334
}
335335
};
336336

337-
impl crate::db::identity::Resource for #struct_name {
337+
impl ::nexus_types::identity::Resource for #struct_name {
338338
fn id(&self) -> ::uuid::Uuid {
339339
self.identity.id
340340
}
@@ -381,7 +381,7 @@ fn build_asset_impl(
381381
}
382382
};
383383

384-
impl crate::db::identity::Asset for #struct_name {
384+
impl ::nexus_types::identity::Asset for #struct_name {
385385
fn id(&self) -> ::uuid::Uuid {
386386
self.identity.id
387387
}

nexus/db-macros/src/lookup.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ fn generate_misc_helpers(config: &Config) -> TokenStream {
389389
/// Build the `authz` object for this resource
390390
fn make_authz(
391391
authz_parent: &authz::#parent_resource_name,
392-
db_row: &model::#resource_name,
392+
db_row: &nexus_db_model::#resource_name,
393393
lookup_type: LookupType,
394394
) -> authz::#resource_name {
395395
authz::#resource_name::new(
@@ -533,7 +533,7 @@ fn generate_lookup_methods(config: &Config) -> TokenStream {
533533
/// This is equivalent to `fetch_for(authz::Action::Read)`.
534534
pub async fn fetch(
535535
&self,
536-
) -> LookupResult<(#(authz::#path_types,)* model::#resource_name)> {
536+
) -> LookupResult<(#(authz::#path_types,)* nexus_db_model::#resource_name)> {
537537
self.fetch_for(authz::Action::Read).await
538538
}
539539

@@ -549,7 +549,7 @@ fn generate_lookup_methods(config: &Config) -> TokenStream {
549549
pub async fn fetch_for(
550550
&self,
551551
action: authz::Action,
552-
) -> LookupResult<(#(authz::#path_types,)* model::#resource_name)> {
552+
) -> LookupResult<(#(authz::#path_types,)* nexus_db_model::#resource_name)> {
553553
let lookup = self.lookup_root();
554554
let opctx = &lookup.opctx;
555555
let datastore = &lookup.datastore;
@@ -712,7 +712,7 @@ fn generate_database_functions(config: &Config) -> TokenStream {
712712
#parent_lookup_arg_formal
713713
name: &Name,
714714
action: authz::Action,
715-
) -> LookupResult<(authz::#resource_name, model::#resource_name)> {
715+
) -> LookupResult<(authz::#resource_name, nexus_db_model::#resource_name)> {
716716
let (#resource_authz_name, db_row) =
717717
Self::lookup_by_name_no_authz(
718718
opctx,
@@ -738,15 +738,15 @@ fn generate_database_functions(config: &Config) -> TokenStream {
738738
#parent_lookup_arg_formal
739739
name: &Name,
740740
) -> LookupResult<
741-
(authz::#resource_name, model::#resource_name)
741+
(authz::#resource_name, nexus_db_model::#resource_name)
742742
> {
743743
use db::schema::#resource_as_snake::dsl;
744744

745745
dsl::#resource_as_snake
746746
#soft_delete_filter
747747
.filter(dsl::name.eq(name.clone()))
748748
#lookup_filter
749-
.select(model::#resource_name::as_select())
749+
.select(nexus_db_model::#resource_name::as_select())
750750
.get_result_async(
751751
datastore.pool_authorized(opctx).await?
752752
)
@@ -804,7 +804,7 @@ fn generate_database_functions(config: &Config) -> TokenStream {
804804
datastore: &DataStore,
805805
#(#pkey_names: &#pkey_types,)*
806806
action: authz::Action,
807-
) -> LookupResult<(#(authz::#path_types,)* model::#resource_name)> {
807+
) -> LookupResult<(#(authz::#path_types,)* nexus_db_model::#resource_name)> {
808808
let (#(#path_authz_names,)* db_row) =
809809
Self::lookup_by_id_no_authz(
810810
opctx,
@@ -826,13 +826,13 @@ fn generate_database_functions(config: &Config) -> TokenStream {
826826
opctx: &OpContext,
827827
datastore: &DataStore,
828828
#(#pkey_names: &#pkey_types,)*
829-
) -> LookupResult<(#(authz::#path_types,)* model::#resource_name)> {
829+
) -> LookupResult<(#(authz::#path_types,)* nexus_db_model::#resource_name)> {
830830
use db::schema::#resource_as_snake::dsl;
831831

832832
let db_row = dsl::#resource_as_snake
833833
#soft_delete_filter
834834
#(.filter(dsl::#pkey_column_names.eq(#pkey_names.clone())))*
835-
.select(model::#resource_name::as_select())
835+
.select(nexus_db_model::#resource_name::as_select())
836836
.get_result_async(datastore.pool_authorized(opctx).await?)
837837
.await
838838
.map_err(|e| {

nexus/db-model/Cargo.toml

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[package]
2+
name = "nexus-db-model"
3+
version = "0.1.0"
4+
edition = "2021"
5+
license = "MPL-2.0"
6+
7+
[dependencies]
8+
anyhow = "1.0"
9+
chrono = { version = "0.4", features = ["serde"] }
10+
diesel = { version = "2.0.0-rc.1", features = ["postgres", "r2d2", "chrono", "serde_json", "network-address", "uuid"] }
11+
hex = "0.4.3"
12+
ipnetwork = "0.18"
13+
macaddr = { version = "1.0.1", features = [ "serde_std" ]}
14+
newtype_derive = "0.1.6"
15+
parse-display = "0.5.4"
16+
rand = "0.8.5"
17+
ref-cast = "1.0"
18+
schemars = { version = "0.8.10", features = ["chrono", "uuid1"] }
19+
serde = { version = "1.0", features = ["derive"] }
20+
serde_json = "1.0"
21+
uuid = { version = "1.1.0", features = ["serde", "v4"] }
22+
23+
steno = { git = "https://github.com/oxidecomputer/steno", branch = "main" }
24+
25+
db-macros = { path = "../db-macros" }
26+
omicron-common = { path = "../../common" }
27+
nexus-defaults = { path = "../defaults" }
28+
nexus-types = { path = "../types" }
29+
sled-agent-client = { path = "../../sled-agent-client" }

nexus/src/db/model/block_size.rs renamed to nexus/db-model/src/block_size.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

55
use super::impl_enum_type;
6-
use crate::external_api::params;
6+
use nexus_types::external_api::params;
77
use omicron_common::api::external;
88
use serde::{Deserialize, Serialize};
99

File renamed without changes.

nexus/db-model/src/collection.rs

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
use diesel::pg::Pg;
6+
use diesel::Column;
7+
use diesel::ExpressionMethods;
8+
use diesel::Selectable;
9+
use std::fmt::Debug;
10+
11+
/// Trait to be implemented by any structs representing a collection.
12+
/// For example, since Organizations have a one-to-many relationship with
13+
/// Projects, the Organization datatype should implement this trait.
14+
/// ```
15+
/// # use diesel::prelude::*;
16+
/// # use nexus_db_model::DatastoreCollectionConfig;
17+
/// # use nexus_db_model::Generation;
18+
/// #
19+
/// # table! {
20+
/// # test_schema.organization (id) {
21+
/// # id -> Uuid,
22+
/// # time_deleted -> Nullable<Timestamptz>,
23+
/// # rcgen -> Int8,
24+
/// # }
25+
/// # }
26+
/// #
27+
/// # table! {
28+
/// # test_schema.project (id) {
29+
/// # id -> Uuid,
30+
/// # time_deleted -> Nullable<Timestamptz>,
31+
/// # organization_id -> Uuid,
32+
/// # }
33+
/// # }
34+
///
35+
/// #[derive(Queryable, Insertable, Debug, Selectable)]
36+
/// #[diesel(table_name = project)]
37+
/// struct Project {
38+
/// pub id: uuid::Uuid,
39+
/// pub time_deleted: Option<chrono::DateTime<chrono::Utc>>,
40+
/// pub organization_id: uuid::Uuid,
41+
/// }
42+
///
43+
/// #[derive(Queryable, Insertable, Debug, Selectable)]
44+
/// #[diesel(table_name = organization)]
45+
/// struct Organization {
46+
/// pub id: uuid::Uuid,
47+
/// pub time_deleted: Option<chrono::DateTime<chrono::Utc>>,
48+
/// pub rcgen: Generation,
49+
/// }
50+
///
51+
/// impl DatastoreCollectionConfig<Project> for Organization {
52+
/// // Type of Organization::identity::id and Project::organization_id
53+
/// type CollectionId = uuid::Uuid;
54+
///
55+
/// type GenerationNumberColumn = organization::dsl::rcgen;
56+
/// type CollectionTimeDeletedColumn = organization::dsl::time_deleted;
57+
///
58+
/// type CollectionIdColumn = project::dsl::organization_id;
59+
/// }
60+
/// ```
61+
pub trait DatastoreCollectionConfig<ResourceType> {
62+
/// The Rust type of the collection id (typically Uuid for us)
63+
type CollectionId: Copy + Debug;
64+
65+
/// The column in the CollectionTable that acts as a generation number.
66+
/// This is the "child-resource-generation-number" in RFD 192.
67+
type GenerationNumberColumn: Column + Default;
68+
69+
/// The time deleted column in the CollectionTable
70+
// We enforce that this column comes from the same table as
71+
// GenerationNumberColumn when defining insert_resource() below.
72+
type CollectionTimeDeletedColumn: Column + Default;
73+
74+
/// The column in the ResourceTable that acts as a foreign key into
75+
/// the CollectionTable
76+
type CollectionIdColumn: Column;
77+
}
78+
79+
/// Trait to be implemented by structs representing an attachable collection.
80+
///
81+
/// For example, since Instances have a one-to-many relationship with
82+
/// Disks, the Instance datatype should implement this trait.
83+
/// ```
84+
/// # use diesel::prelude::*;
85+
/// # use nexus_db_model::DatastoreAttachTargetConfig;
86+
/// #
87+
/// # table! {
88+
/// # test_schema.instance (id) {
89+
/// # id -> Uuid,
90+
/// # time_deleted -> Nullable<Timestamptz>,
91+
/// # }
92+
/// # }
93+
/// #
94+
/// # table! {
95+
/// # test_schema.disk (id) {
96+
/// # id -> Uuid,
97+
/// # time_deleted -> Nullable<Timestamptz>,
98+
/// # instance_id -> Nullable<Uuid>,
99+
/// # }
100+
/// # }
101+
///
102+
/// #[derive(Queryable, Debug, Selectable)]
103+
/// #[diesel(table_name = disk)]
104+
/// struct Disk {
105+
/// pub id: uuid::Uuid,
106+
/// pub time_deleted: Option<chrono::DateTime<chrono::Utc>>,
107+
/// pub instance_id: Option<uuid::Uuid>,
108+
/// }
109+
///
110+
/// #[derive(Queryable, Debug, Selectable)]
111+
/// #[diesel(table_name = instance)]
112+
/// struct Instance {
113+
/// pub id: uuid::Uuid,
114+
/// pub time_deleted: Option<chrono::DateTime<chrono::Utc>>,
115+
/// }
116+
///
117+
/// impl DatastoreAttachTargetConfig<Disk> for Instance {
118+
/// // Type of instance::id and disk::id.
119+
/// type Id = uuid::Uuid;
120+
///
121+
/// type CollectionIdColumn = instance::dsl::id;
122+
/// type CollectionTimeDeletedColumn = instance::dsl::time_deleted;
123+
///
124+
/// type ResourceIdColumn = disk::dsl::id;
125+
/// type ResourceCollectionIdColumn = disk::dsl::instance_id;
126+
/// type ResourceTimeDeletedColumn = disk::dsl::time_deleted;
127+
/// }
128+
/// ```
129+
pub trait DatastoreAttachTargetConfig<ResourceType>:
130+
Selectable<Pg> + Sized
131+
{
132+
/// The Rust type of the collection and resource ids (typically Uuid).
133+
type Id: Copy + Debug + PartialEq + Send + 'static;
134+
135+
/// The primary key column of the collection.
136+
type CollectionIdColumn: Column;
137+
138+
/// The time deleted column in the CollectionTable
139+
type CollectionTimeDeletedColumn: Column<Table = <Self::CollectionIdColumn as Column>::Table>
140+
+ Default
141+
+ ExpressionMethods;
142+
143+
/// The primary key column of the resource
144+
type ResourceIdColumn: Column;
145+
146+
/// The column in the resource acting as a foreign key into the Collection
147+
type ResourceCollectionIdColumn: Column<Table = <Self::ResourceIdColumn as Column>::Table>
148+
+ Default
149+
+ ExpressionMethods;
150+
151+
/// The time deleted column in the ResourceTable
152+
type ResourceTimeDeletedColumn: Column<Table = <Self::ResourceIdColumn as Column>::Table>
153+
+ Default
154+
+ ExpressionMethods;
155+
}

nexus/src/db/model/console_session.rs renamed to nexus/db-model/src/console_session.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
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::db::schema::console_session;
5+
use crate::schema::console_session;
66
use chrono::{DateTime, Utc};
77
use uuid::Uuid;
88

nexus/src/db/model/dataset.rs renamed to nexus/db-model/src/dataset.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

55
use super::{DatasetKind, Generation, Region, SqlU16};
6-
use crate::db::collection_insert::DatastoreCollection;
7-
use crate::db::schema::{dataset, region};
6+
use crate::collection::DatastoreCollectionConfig;
7+
use crate::schema::{dataset, region};
88
use chrono::{DateTime, Utc};
99
use db_macros::Asset;
1010
use serde::{Deserialize, Serialize};
@@ -75,7 +75,7 @@ impl Dataset {
7575
}
7676

7777
// Datasets contain regions
78-
impl DatastoreCollection<Region> for Dataset {
78+
impl DatastoreCollectionConfig<Region> for Dataset {
7979
type CollectionId = Uuid;
8080
type GenerationNumberColumn = dataset::dsl::rcgen;
8181
type CollectionTimeDeletedColumn = dataset::dsl::time_deleted;

nexus/src/db/model/dataset_kind.rs renamed to nexus/db-model/src/dataset_kind.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

55
use super::impl_enum_type;
6-
use crate::internal_api;
6+
use nexus_types::internal_api;
77
use serde::{Deserialize, Serialize};
88

99
impl_enum_type!(

0 commit comments

Comments
 (0)