Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
4610d1a
first cut
davepacheco Mar 20, 2022
ef932a1
rename Lookup
davepacheco Mar 20, 2022
9cc52b0
formatting nit
davepacheco Mar 20, 2022
5845149
formatting
davepacheco Mar 20, 2022
49a10e3
fix
davepacheco Mar 20, 2022
491f30c
some notes, PoC diesel impl
davepacheco Mar 20, 2022
037b78f
get rid of Root
davepacheco Mar 21, 2022
ebaefb1
make LookupPath reachable through the whole path
davepacheco Mar 21, 2022
ce4b362
some cleanup
davepacheco Mar 21, 2022
aae2da4
flesh out macros
davepacheco Mar 21, 2022
73f0f4b
more macro cleanup
davepacheco Mar 21, 2022
68b51cf
more cleanup
davepacheco Mar 21, 2022
6d265bb
more commonizing
davepacheco Mar 21, 2022
e4a5844
cleanup
davepacheco Mar 21, 2022
0e73f25
more commonizing
davepacheco Mar 21, 2022
a414c1f
lookup done enough to start trying it out
davepacheco Mar 21, 2022
c7941b3
prototype a few call sites in Nexus
davepacheco Mar 21, 2022
5690db3
some basic tests are working by accident
davepacheco Mar 21, 2022
295182b
cleanup
davepacheco Mar 21, 2022
bcf57a6
fix up prototyping
davepacheco Mar 21, 2022
43d0102
replace one XXX with a comment
davepacheco Mar 21, 2022
0a494ae
in-progress update to return entire path -- cannot work with macro_ru…
davepacheco Mar 22, 2022
013e981
it works with macro_rules!
davepacheco Mar 22, 2022
785eda2
half-finished effort at proc macro approah
davepacheco Mar 25, 2022
a48722a
more progress
davepacheco Mar 25, 2022
dd5ce40
more work
davepacheco Mar 26, 2022
1f1615c
closer to compiling
davepacheco Mar 26, 2022
4fede01
closer to compiling
davepacheco Mar 26, 2022
30ecdc8
nearly fixed
davepacheco Mar 28, 2022
9f557db
compiles!
davepacheco Mar 28, 2022
064e64d
generate child lookup functions
davepacheco Mar 28, 2022
f2d5770
rip out dead code, fix test
davepacheco Mar 28, 2022
c222c7b
clippy
davepacheco Mar 29, 2022
019d15f
make it a function-like macro
davepacheco Mar 29, 2022
de35510
remove dead code
davepacheco Mar 29, 2022
872ea9a
re-add Nexus changes
davepacheco Mar 29, 2022
44acb43
add some docs
davepacheco Mar 29, 2022
51b0af8
documentation for `LookupPath`
davepacheco Mar 30, 2022
9897ca2
more docs
davepacheco Mar 30, 2022
af6fca3
more docs
davepacheco Mar 30, 2022
977b9c0
more documentation
davepacheco Mar 30, 2022
58b03ed
more cleanup
davepacheco Mar 30, 2022
d68e1c5
macro impl cleanup: part 1 (naming)
davepacheco Mar 30, 2022
cf6591e
more extraction
davepacheco Mar 30, 2022
718b0a0
more cleanup
davepacheco Mar 30, 2022
ce6066d
more cleanup
davepacheco Mar 30, 2022
2d5353d
macro impl: commonize Resource
davepacheco Mar 30, 2022
a71e1a1
macro impl: no need to special-case authz names
davepacheco Mar 30, 2022
e25e9aa
macro impl: minor cleanup
davepacheco Mar 30, 2022
7230871
macro impl: no need to first-class model type
davepacheco Mar 30, 2022
07eaab8
macro impl: authz type need not be first-classed
davepacheco Mar 30, 2022
ea67303
clippy
davepacheco Mar 30, 2022
62e5eea
macro impl: add comment
davepacheco Mar 30, 2022
32eab6e
Merge branch 'main' into new-lookup
davepacheco Mar 30, 2022
fa0f1d6
fix mismerge
davepacheco Mar 30, 2022
6f9411c
convert route endpoints to new lookup API
davepacheco Mar 31, 2022
d3c9f1d
fixes
davepacheco Mar 31, 2022
1cb9235
fix style
davepacheco Mar 31, 2022
386549a
oh right, we can remove the hand-written database functions now
davepacheco Mar 31, 2022
342771c
convert routers to new lookup API
davepacheco Mar 31, 2022
1ef75df
Merge remote-tracking branch 'origin/main' into convert-new-lookup
davepacheco Mar 31, 2022
e2efc57
Merge branch 'convert-new-lookup' into convert-new-lookup-routers
davepacheco Mar 31, 2022
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 @@ -1256,7 +1256,7 @@ pub struct RouterRoute {
pub identity: IdentityMetadata,

/// The VPC Router to which the route belongs.
pub router_id: Uuid,
pub vpc_router_id: Uuid,

/// Describes the kind of router. Set at creation. `read-only`
pub kind: RouterRouteKind,
Expand Down
4 changes: 2 additions & 2 deletions common/src/sql/dbinit.sql
Original file line number Diff line number Diff line change
Expand Up @@ -687,14 +687,14 @@ CREATE TABLE omicron.public.router_route (
/* Indicates that the object has been deleted */
time_deleted TIMESTAMPTZ,

router_id UUID NOT NULL,
vpc_router_id UUID NOT NULL,
kind omicron.public.router_route_kind NOT NULL,
target STRING(128) NOT NULL,
destination STRING(128) NOT NULL
);

CREATE UNIQUE INDEX ON omicron.public.router_route (
router_id,
vpc_router_id,
name
) WHERE
time_deleted IS NULL;
Expand Down
162 changes: 3 additions & 159 deletions nexus/src/db/datastore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2683,81 +2683,6 @@ impl DataStore {
.map_err(|e| public_error_from_diesel_pool(e, ErrorHandler::Server))
}

/// Fetches a VpcRouter from the database and returns both the database row
/// and an [`authz::VpcRouter`] for doing authz checks
///
/// See [`DataStore::organization_lookup_noauthz()`] for intended use cases
/// and caveats.
// TODO-security See the note on organization_lookup_noauthz().
async fn vpc_router_lookup_noauthz(
&self,
authz_vpc: &authz::Vpc,
router_name: &Name,
) -> LookupResult<(authz::VpcRouter, VpcRouter)> {
use db::schema::vpc_router::dsl;
dsl::vpc_router
.filter(dsl::time_deleted.is_null())
.filter(dsl::vpc_id.eq(authz_vpc.id()))
.filter(dsl::name.eq(router_name.clone()))
.select(VpcRouter::as_select())
.get_result_async(self.pool())
.await
.map_err(|e| {
public_error_from_diesel_pool(
e,
ErrorHandler::NotFoundByLookup(
ResourceType::VpcRouter,
LookupType::ByName(router_name.as_str().to_owned()),
),
)
})
.map(|r| {
(
authz_vpc.child_generic(
ResourceType::VpcRouter,
r.id(),
LookupType::ByName(router_name.to_string()),
),
r,
)
})
}

/// Lookup a VpcRouter by name and return the full database record, along
/// with an [`authz::VpcRouter`] for subsequent authorization checks
pub async fn vpc_router_fetch(
&self,
opctx: &OpContext,
authz_vpc: &authz::Vpc,
name: &Name,
) -> LookupResult<(authz::VpcRouter, VpcRouter)> {
let (authz_vpc_router, db_vpc_router) =
self.vpc_router_lookup_noauthz(authz_vpc, name).await?;
opctx.authorize(authz::Action::Read, &authz_vpc_router).await?;
Ok((authz_vpc_router, db_vpc_router))
}

/// Look up the id for a VpcRouter based on its name
///
/// Returns an [`authz::VpcRouter`] (which makes the id available).
///
/// Like the other "lookup_by_path()" functions, this function does no authz
/// checks.
pub async fn vpc_router_lookup_by_path(
&self,
organization_name: &Name,
project_name: &Name,
vpc_name: &Name,
router_name: &Name,
) -> LookupResult<authz::VpcRouter> {
let authz_vpc = self
.vpc_lookup_by_path(organization_name, project_name, vpc_name)
.await?;
self.vpc_router_lookup_noauthz(&authz_vpc, router_name)
.await
.map(|(v, _)| v)
}

pub async fn vpc_create_router(
&self,
opctx: &OpContext,
Expand Down Expand Up @@ -2854,7 +2779,7 @@ impl DataStore {
use db::schema::router_route::dsl;
paginated(dsl::router_route, dsl::name, pagparams)
.filter(dsl::time_deleted.is_null())
.filter(dsl::router_id.eq(authz_router.id()))
.filter(dsl::vpc_router_id.eq(authz_router.id()))
.select(RouterRoute::as_select())
.load_async::<db::model::RouterRoute>(
self.pool_authorized(opctx).await?,
Expand All @@ -2863,98 +2788,17 @@ impl DataStore {
.map_err(|e| public_error_from_diesel_pool(e, ErrorHandler::Server))
}

/// Fetches a RouterRoute from the database and returns both the database
/// row and an [`authz::RouterRoute`] for doing authz checks
///
/// See [`DataStore::organization_lookup_noauthz()`] for intended use cases
/// and caveats.
// TODO-security See the note on organization_lookup_noauthz().
async fn route_lookup_noauthz(
&self,
authz_vpc_router: &authz::VpcRouter,
route_name: &Name,
) -> LookupResult<(authz::RouterRoute, RouterRoute)> {
use db::schema::router_route::dsl;
dsl::router_route
.filter(dsl::time_deleted.is_null())
.filter(dsl::router_id.eq(authz_vpc_router.id()))
.filter(dsl::name.eq(route_name.clone()))
.select(RouterRoute::as_select())
.get_result_async(self.pool())
.await
.map_err(|e| {
public_error_from_diesel_pool(
e,
ErrorHandler::NotFoundByLookup(
ResourceType::RouterRoute,
LookupType::ByName(route_name.as_str().to_owned()),
),
)
})
.map(|r| {
(
authz_vpc_router.child_generic(
ResourceType::RouterRoute,
r.id(),
LookupType::ByName(route_name.to_string()),
),
r,
)
})
}

/// Lookup a RouterRoute by name and return the full database record, along
/// with an [`authz::RouterRoute`] for subsequent authorization checks
pub async fn route_fetch(
&self,
opctx: &OpContext,
authz_vpc_router: &authz::VpcRouter,
name: &Name,
) -> LookupResult<(authz::RouterRoute, RouterRoute)> {
let (authz_route, db_route) =
self.route_lookup_noauthz(authz_vpc_router, name).await?;
opctx.authorize(authz::Action::Read, &authz_route).await?;
Ok((authz_route, db_route))
}

/// Look up the id for a RouterRoute based on its name
///
/// Returns an [`authz::RouterRoute`] (which makes the id available).
///
/// Like the other "lookup_by_path()" functions, this function does no authz
/// checks.
pub async fn route_lookup_by_path(
&self,
organization_name: &Name,
project_name: &Name,
vpc_name: &Name,
router_name: &Name,
route_name: &Name,
) -> LookupResult<authz::RouterRoute> {
let authz_vpc_router = self
.vpc_router_lookup_by_path(
organization_name,
project_name,
vpc_name,
router_name,
)
.await?;
self.vpc_router_lookup_noauthz(&authz_vpc_router, route_name)
.await
.map(|(v, _)| v)
}

pub async fn router_create_route(
&self,
opctx: &OpContext,
authz_router: &authz::VpcRouter,
route: RouterRoute,
) -> CreateResult<RouterRoute> {
assert_eq!(authz_router.id(), route.router_id);
assert_eq!(authz_router.id(), route.vpc_router_id);
opctx.authorize(authz::Action::CreateChild, authz_router).await?;

use db::schema::router_route::dsl;
let router_id = route.router_id;
let router_id = route.vpc_router_id;
let name = route.name().clone();

VpcRouter::insert_resource(
Expand Down
38 changes: 37 additions & 1 deletion nexus/src/db/lookup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,21 @@ impl<'a> LookupPath<'a> {
pub fn disk_id(self, id: Uuid) -> Disk<'a> {
Disk { key: Key::Id(Root { lookup_root: self }, id) }
}

/// Select a resource of type Vpc, identified by its id
pub fn vpc_id(self, id: Uuid) -> Vpc<'a> {
Vpc { key: Key::Id(Root { lookup_root: self }, id) }
}

/// Select a resource of type VpcRouter, identified by its id
pub fn vpc_router_id(self, id: Uuid) -> VpcRouter<'a> {
VpcRouter { key: Key::Id(Root { lookup_root: self }, id) }
}

/// Select a resource of type RouterRoute, identified by its id
pub fn router_route_id(self, id: Uuid) -> RouterRoute<'a> {
RouterRoute { key: Key::Id(Root { lookup_root: self }, id) }
}
}

/// Describes a node along the selection path of a resource
Expand Down Expand Up @@ -244,7 +259,7 @@ lookup_resource! {
lookup_resource! {
name = "Project",
ancestors = [ "Organization" ],
children = [ "Disk", "Instance" ],
children = [ "Disk", "Instance", "Vpc" ],
authz_kind = Typed
}

Expand All @@ -262,6 +277,27 @@ lookup_resource! {
authz_kind = Generic
}

lookup_resource! {
name = "Vpc",
ancestors = [ "Organization", "Project" ],
children = [ "VpcRouter" ],
authz_kind = Generic
}

lookup_resource! {
name = "VpcRouter",
ancestors = [ "Organization", "Project", "Vpc" ],
children = [ "RouterRoute" ],
authz_kind = Generic
}

lookup_resource! {
name = "RouterRoute",
ancestors = [ "Organization", "Project", "Vpc", "VpcRouter" ],
children = [],
authz_kind = Generic
}

#[cfg(test)]
mod test {
use super::Instance;
Expand Down
10 changes: 5 additions & 5 deletions nexus/src/db/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1810,7 +1810,7 @@ impl DatastoreCollection<RouterRoute> for VpcRouter {
type CollectionId = Uuid;
type GenerationNumberColumn = vpc_router::dsl::rcgen;
type CollectionTimeDeletedColumn = vpc_router::dsl::time_deleted;
type CollectionIdColumn = router_route::dsl::router_id;
type CollectionIdColumn = router_route::dsl::vpc_router_id;
}

#[derive(AsChangeset)]
Expand Down Expand Up @@ -1922,22 +1922,22 @@ pub struct RouterRoute {
identity: RouterRouteIdentity,

pub kind: RouterRouteKind,
pub router_id: Uuid,
pub vpc_router_id: Uuid,
pub target: RouteTarget,
pub destination: RouteDestination,
}

impl RouterRoute {
pub fn new(
route_id: Uuid,
router_id: Uuid,
vpc_router_id: Uuid,
kind: external::RouterRouteKind,
params: external::RouterRouteCreateParams,
) -> Self {
let identity = RouterRouteIdentity::new(route_id, params.identity);
Self {
identity,
router_id,
vpc_router_id,
kind: RouterRouteKind(kind),
target: RouteTarget(params.target),
destination: RouteDestination::new(params.destination),
Expand All @@ -1949,7 +1949,7 @@ impl Into<external::RouterRoute> for RouterRoute {
fn into(self) -> external::RouterRoute {
external::RouterRoute {
identity: self.identity(),
router_id: self.router_id,
vpc_router_id: self.vpc_router_id,
kind: self.kind.0,
target: self.target.0.clone(),
destination: self.destination.state().clone(),
Expand Down
2 changes: 1 addition & 1 deletion nexus/src/db/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ table! {
time_modified -> Timestamptz,
time_deleted -> Nullable<Timestamptz>,
kind -> crate::db::model::RouterRouteKindEnum,
router_id -> Uuid,
vpc_router_id -> Uuid,
target -> Text,
destination -> Text,
}
Expand Down
Loading