From 81369d71762f7b4af8c04fecfd397de926500d3f Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Fri, 24 Jun 2022 00:34:34 -0400 Subject: [PATCH 01/18] first pass at implementing /by_id/ routes --- nexus/src/app/disk.rs | 19 +++ nexus/src/app/image.rs | 16 ++ nexus/src/app/instance.rs | 15 ++ nexus/src/app/organization.rs | 12 ++ nexus/src/app/project.rs | 12 ++ nexus/src/app/vpc.rs | 7 + nexus/src/app/vpc_router.rs | 16 ++ nexus/src/app/vpc_subnet.rs | 8 + nexus/src/authz/api_resources.rs | 16 ++ nexus/src/db/lookup.rs | 35 ++++ nexus/src/external_api/http_entrypoints.rs | 185 ++++++++++++++++++++- 11 files changed, 332 insertions(+), 9 deletions(-) diff --git a/nexus/src/app/disk.rs b/nexus/src/app/disk.rs index dd4aade4df..3ff5aac3ca 100644 --- a/nexus/src/app/disk.rs +++ b/nexus/src/app/disk.rs @@ -214,6 +214,18 @@ impl super::Nexus { Ok(db_disk) } + pub async fn disk_fetch_by_id( + &self, + opctx: &OpContext, + disk_id: Uuid, + ) -> LookupResult { + let (.., db_disk) = LookupPath::new(opctx, &self.db_datastore) + .disk_id(disk_id) + .fetch() + .await?; + Ok(db_disk) + } + /// Modifies the runtime state of the Disk as requested. This generally /// means attaching or detaching the disk. // TODO(https://github.com/oxidecomputer/omicron/issues/811): @@ -386,6 +398,13 @@ impl super::Nexus { Err(self.unimplemented_todo(opctx, unimp).await) } + pub async fn snapshot_fetch_by_id(&self, opctx: &OpContext, id: Uuid) -> LookupResult { + let (.., db_snapshot) = LookupPath::new(opctx, &self.db_datastore) + .snapshot_id(id) + .fetch() + .await?; + } + pub async fn project_delete_snapshot( self: &Arc, opctx: &OpContext, diff --git a/nexus/src/app/image.rs b/nexus/src/app/image.rs index c3ef736afc..622fcce550 100644 --- a/nexus/src/app/image.rs +++ b/nexus/src/app/image.rs @@ -71,6 +71,14 @@ impl super::Nexus { Err(self.unimplemented_todo(opctx, unimp).await) } + pub async fn project_image_fetch_by_id(&self, opctx: &OpContext, image_id: Uuid) -> LookupResult { + let (.., db_image) = LookupPath::new(opctx, &self.db_datastore) + .image_id(image_id) + .fetch() + .await?; + Ok(db_image) + } + pub async fn project_delete_image( self: &Arc, opctx: &OpContext, @@ -296,6 +304,14 @@ impl super::Nexus { Ok(db_disk) } + pub async fn global_image_fetch_by_id(&self, opctx: &OpContext, global_image_id: Uuid) -> LookupResult { + let (.., db_global_image) = LookupPath::new(opctx, &self.db_datastore) + .global_image_id(global_image_id) + .fetch() + .await?; + Ok(db_global_image) + } + pub async fn global_image_delete( self: &Arc, opctx: &OpContext, diff --git a/nexus/src/app/instance.rs b/nexus/src/app/instance.rs index 3d5d3d4220..6df4edaf2c 100644 --- a/nexus/src/app/instance.rs +++ b/nexus/src/app/instance.rs @@ -178,6 +178,14 @@ impl super::Nexus { Ok(db_instance) } + pub async fn instance_fetch_by_id(&self, opctx: &OpContext, instance_id: Uuid) -> LookupResult { + let (.., db_instance) = LookupPath::new(opctx, &self.db_datastore) + .instance_id(instance_id) + .fetch() + .await?; + Ok(db_instance) + } + // This operation may only occur on stopped instances, which implies that // the attached disks do not have any running "upstairs" process running // within the sled. @@ -760,6 +768,13 @@ impl super::Nexus { Ok(db_interface) } + pub async fn network_interface_fetch_by_id(&self, opctx: &OpContext, interface_id: Uuid) -> LookupResult { + let (.., db_interface) = LookupPath::new(opctx, &self.db_datastore) + .network_interface_id(interface_id) + .await?; + Ok(db_interface) + } + /// Update a network interface for the given instance. pub async fn network_interface_update( &self, diff --git a/nexus/src/app/organization.rs b/nexus/src/app/organization.rs index bcb2e90cbf..3d343f682e 100644 --- a/nexus/src/app/organization.rs +++ b/nexus/src/app/organization.rs @@ -42,6 +42,18 @@ impl super::Nexus { Ok(db_organization) } + pub async fn organization_fetch_by_id( + &self, + opctx: &OpContext, + organization_id: Uuid, + ) -> LookupResult { + let (.., db_organization) = LookupPath::new(opctx, &self.db_datastore) + .organization_id(organization_id) + .fetch() + .await?; + Ok(db_organization) + } + pub async fn organizations_list_by_name( &self, opctx: &OpContext, diff --git a/nexus/src/app/project.rs b/nexus/src/app/project.rs index f6fca8f82f..ddad6c0d03 100644 --- a/nexus/src/app/project.rs +++ b/nexus/src/app/project.rs @@ -88,6 +88,18 @@ impl super::Nexus { Ok(db_project) } + pub async fn project_fetch_by_id( + &self, + opctx: &OpContext, + project_id: Uuid, + ) -> LookupResult { + let (.., db_project) = LookupPath::new(opctx, &self.db_datastore) + .project_id(project_id) + .fetch() + .await?; + Ok(db_project) + } + pub async fn projects_list_by_name( &self, opctx: &OpContext, diff --git a/nexus/src/app/vpc.rs b/nexus/src/app/vpc.rs index fe6d58d4c8..74961e77dc 100644 --- a/nexus/src/app/vpc.rs +++ b/nexus/src/app/vpc.rs @@ -209,6 +209,13 @@ impl super::Nexus { Ok(db_vpc) } + pub async fn vpc_fetch_by_id(&self, opctx: &OpContext, vpc_id: Uuid) -> LookupResult { + let (.., db_vpc) = LookupPath::new(opctx, &self.db_datastore) + .vpc_id(vpc_id) + .await?; + Ok(db_vpc) + } + pub async fn project_update_vpc( &self, opctx: &OpContext, diff --git a/nexus/src/app/vpc_router.rs b/nexus/src/app/vpc_router.rs index 20b38e4233..5f4f35489f 100644 --- a/nexus/src/app/vpc_router.rs +++ b/nexus/src/app/vpc_router.rs @@ -96,6 +96,14 @@ impl super::Nexus { Ok(db_router) } + pub async fn vpc_router_fetch_by_id(&self, opctx: &OpContext, vpc_router_id: Uuid) -> LookupResult { + let (.., db_router) = LookupPath::new(opctx, &self.db_datastore) + .vpc_router_id(vpc_router_id) + .fetch() + .await?; + Ok(db_router) + } + pub async fn vpc_update_router( &self, opctx: &OpContext, @@ -223,6 +231,14 @@ impl super::Nexus { Ok(db_route) } + pub async fn route_fetch_by_id(&self, opctx: &OpContext, route_id: Uuid) -> LookupResult { + let (.., db_route) = LookupPath::new(opctx, &self.db_datastore) + .router_route_id(route_id) + .fetch() + .await?; + Ok(db_route) + } + #[allow(clippy::too_many_arguments)] pub async fn router_update_route( &self, diff --git a/nexus/src/app/vpc_subnet.rs b/nexus/src/app/vpc_subnet.rs index 84d7f458ee..b1def2aba3 100644 --- a/nexus/src/app/vpc_subnet.rs +++ b/nexus/src/app/vpc_subnet.rs @@ -218,6 +218,14 @@ impl super::Nexus { Ok(db_vpc) } + pub async fn vpc_subnet_fetch_by_id(&self, opctx: &OpContext, vpc_subnet_id: Uuid) -> LookupResult { + let (.., db_vpc) = LookupPath::new(opctx, &self.db_datastore) + .vpc_subnet_id(vpc_subnet_id) + .fetch() + .await?; + Ok(db_vpc) + } + pub async fn vpc_update_subnet( &self, opctx: &OpContext, diff --git a/nexus/src/authz/api_resources.rs b/nexus/src/authz/api_resources.rs index 486414dd1c..558d167f61 100644 --- a/nexus/src/authz/api_resources.rs +++ b/nexus/src/authz/api_resources.rs @@ -482,6 +482,22 @@ authz_resource! { polar_snippet = InProject, } +authz_resource! { + name = "Image", + parent = "Project", + primary_key = Uuid, + roles_allowed = false, + polar_snippet = InProject, +} + +authz_resource! { + name = "Snapshot", + parent = "Project", + primary_key = Uuid, + roles_allowed = false, + polar_snippet = InProject, +} + authz_resource! { name = "Instance", parent = "Project", diff --git a/nexus/src/db/lookup.rs b/nexus/src/db/lookup.rs index 22f3feb5e8..9e3edecda2 100644 --- a/nexus/src/db/lookup.rs +++ b/nexus/src/db/lookup.rs @@ -231,6 +231,23 @@ impl<'a> LookupPath<'a> { Disk { key: DiskKey::PrimaryKey(Root { lookup_root: self }, id) } } + /// Select a resource of type Image, identified by its id + pub fn image_id(self, id: Uuid) -> Image<'a> { + Image { key: ImageKey::PrimaryKey(Root { lookup_root: self }, id) } + } + + /// Select a resource of type Snapshot, identified by its id + pub fn snapshot_id(self, id: Uuid) -> Snapshot<'a> { + Snapshot { key: SnapshotKey::PrimaryKey(Root { lookup_root: self }, id) } + } + + /// Select a resource of type NetworkInterface, identified by its id + pub fn network_interface_id(self, id: Uuid) -> NetworkInterface<'a> { + NetworkInterface { + key: NetworkInterfaceKey::PrimaryKey(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: VpcKey::PrimaryKey(Root { lookup_root: self }, id) } @@ -476,6 +493,24 @@ lookup_resource! { primary_key_columns = [ { column_name = "id", rust_type = Uuid } ] } +lookup_resource! { + name = "Image", + ancestors = [ "Silo", "Organization", "Project" ], + children = [], + lookup_by_name = true, + soft_deletes = true, + primary_key_columns = [ { column_name = "id", rust_type = Uuid } ] +} + +lookup_resource! { + name = "Snapshot", + ancestors = [ "Silo", "Organization", "Project" ], + children = [], + lookup_by_name = true, + soft_deletes = true, + primary_key_columns = [ { column_name = "id", rust_type = Uuid } ] +} + lookup_resource! { name = "Instance", ancestors = [ "Silo", "Organization", "Project" ], diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index b47e6ecf12..d7bf932b78 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -208,6 +208,18 @@ pub fn external_api() -> NexusApiDescription { api.register(console_api::login)?; api.register(console_api::consume_credentials)?; + api.register(by_id_organization_get)?; + api.register(by_id_project_get)?; + api.register(by_id_instance_get)?; + api.register(by_id_disk_get)?; + api.register(by_id_image_get)?; + api.register(by_id_global_image_get)?; + api.register(by_id_snapshot_get)?; + api.register(by_id_vpc_get)?; + api.register(by_id_subnet_get)?; + api.register(by_id_network_interface_get)?; + api.register(by_id_router_get)?; + Ok(()) } @@ -3380,14 +3392,169 @@ async fn sshkeys_delete_key( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } -#[cfg(test)] -mod test { - use super::external_api; - #[test] - fn test_nexus_tag_policy() { - // This will fail if any of the endpoints don't match the policy in - // ./tag-config.json - let _ = external_api(); - } +/// Path parameters for request by id +#[derive(Deserialize, JsonSchema)] +struct ByIdPathParams { + id: Uuid, +} + + +/// Fetch an organization by id +#[endpoint { + method = GET, + path = "/by_id/organizations/{id}", + tags = ["organizations"], +}] +async fn by_id_organization_get( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let organization = nexus.organization_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(organization.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + +/// Fetch a project by id +#[endpoint { + method = GET, + path = "/by_id/projects/{id}", + tags = ["projects"], +}] +async fn by_id_project_get( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let project = nexus.project_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(project.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + +/// Fetch an instance by id +#[endpoint { + method = GET, + path = "/by_id/instances/{id}", + tags = ["instances"], +}] +async fn by_id_instance_get( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let instance = nexus.instance_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(instance.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } + +/// Fetch a disk by id +#[endpoint { + method = GET, + path = "/by_id/disks/{id}", + tags = ["disks"], +}] +async fn by_id_disk_get( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let disk = nexus.disk_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(disk.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + +/// Fetch an image by id +#[endpoint { + method = GET, + path = "/by_id/images/{id}", + tags = ["images"], +}] +async fn by_id_image_get( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let image = nexus.image_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(image.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + +/// Fetch a global image by id +#[endpoint { + method = GET, + path = "/by_id/global-images/{id}", + tags = ["images:global"], +}] +async fn by_id_global_image_get( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let image = nexus.global_image_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(image.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + +/// Fetch a snapshot by id +#[endpoint { + method = GET, + path = "/by_id/snapshots/{id}", + tags = ["snapshots"], +}] +async fn by_id_snapshot_get( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let snapshot = nexus.snapshot_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(snapshot.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + +/// Fetch a network interface by id +#[endpoint { + method = GET, + path = "/by_id/network- \ No newline at end of file From 9d78737b4b4881c78eb6783e8e80de70b6e3ab85 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Fri, 24 Jun 2022 01:03:47 -0400 Subject: [PATCH 02/18] Restore text that failed to get written when I ran out of space --- nexus/src/external_api/http_entrypoints.rs | 108 ++++++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index d7bf932b78..99be3e56f7 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -217,8 +217,9 @@ pub fn external_api() -> NexusApiDescription { api.register(by_id_snapshot_get)?; api.register(by_id_vpc_get)?; api.register(by_id_subnet_get)?; - api.register(by_id_network_interface_get)?; api.register(by_id_router_get)?; + api.register(by_id_route_get)?; + api.register(by_id_network_interface_get)?; Ok(()) } @@ -3557,4 +3558,107 @@ async fn by_id_snapshot_get( /// Fetch a network interface by id #[endpoint { method = GET, - path = "/by_id/network- \ No newline at end of file + path = "/by_id/network-interfaces/{id}", + tags = ["instances"], +}] +async fn by_id_network_interface_get( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let network_interface = nexus + .network_interface_fetch_by_id(&opctx, id) + .await?; + Ok(HttpResponseOk(network_interface.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + +#[endpoint { + method = GET, + path = "/by_id/vpcs/{id}", + tags = ["vpcs"], +}] +async fn by_id_vpc_get( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let vpc = nexus.vpc_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(vpc.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + +#[endpoint { + method = GET, + path = "/by_id/subnets/{id}", + tags = ["subnets"], +}] +async fn by_id_subnet_get( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let subnet = nexus.subnet_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(subnet.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + +#[endpoint { + method = GET, + path = "/by_id/router/{id}", + tags = ["routers"], +}] +async fn by_id_router_get( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let router = nexus.vpc_router_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(router.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + +#[endpoint { + method = GET, + path = "/by_id/route/{id}", + tags = ["routes"] +}] +async fn by_id_route_get( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let route = nexus.route_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(route.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} From 3712cd910b889ce7374f1aa4f691d7a6a3b905d5 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Fri, 24 Jun 2022 01:05:20 -0400 Subject: [PATCH 03/18] Add docstrings --- nexus/src/external_api/http_entrypoints.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index 99be3e56f7..c24477c476 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -3579,6 +3579,7 @@ async fn by_id_network_interface_get( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// Fetch a vpc by id #[endpoint { method = GET, path = "/by_id/vpcs/{id}", @@ -3600,6 +3601,7 @@ async fn by_id_vpc_get( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// Fetch a vpc subnet by id #[endpoint { method = GET, path = "/by_id/subnets/{id}", @@ -3621,6 +3623,7 @@ async fn by_id_subnet_get( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// Fetch a vpc router by id #[endpoint { method = GET, path = "/by_id/router/{id}", @@ -3642,6 +3645,7 @@ async fn by_id_router_get( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// Fetch a vpc router route by id #[endpoint { method = GET, path = "/by_id/route/{id}", From 6b99acfeda6e3c3ebc465440d07bd57a72766aee Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Fri, 24 Jun 2022 01:06:17 -0400 Subject: [PATCH 04/18] Fixup formatting --- nexus/src/app/disk.rs | 6 +++++- nexus/src/app/image.rs | 14 +++++++++++--- nexus/src/app/instance.rs | 12 ++++++++++-- nexus/src/app/vpc.rs | 11 +++++++---- nexus/src/app/vpc_router.rs | 12 ++++++++++-- nexus/src/app/vpc_subnet.rs | 6 +++++- nexus/src/db/lookup.rs | 9 +++++++-- nexus/src/external_api/http_entrypoints.rs | 9 +++------ 8 files changed, 58 insertions(+), 21 deletions(-) diff --git a/nexus/src/app/disk.rs b/nexus/src/app/disk.rs index 3ff5aac3ca..3c5d772614 100644 --- a/nexus/src/app/disk.rs +++ b/nexus/src/app/disk.rs @@ -398,7 +398,11 @@ impl super::Nexus { Err(self.unimplemented_todo(opctx, unimp).await) } - pub async fn snapshot_fetch_by_id(&self, opctx: &OpContext, id: Uuid) -> LookupResult { + pub async fn snapshot_fetch_by_id( + &self, + opctx: &OpContext, + id: Uuid, + ) -> LookupResult { let (.., db_snapshot) = LookupPath::new(opctx, &self.db_datastore) .snapshot_id(id) .fetch() diff --git a/nexus/src/app/image.rs b/nexus/src/app/image.rs index 622fcce550..3a98c20ffa 100644 --- a/nexus/src/app/image.rs +++ b/nexus/src/app/image.rs @@ -71,12 +71,16 @@ impl super::Nexus { Err(self.unimplemented_todo(opctx, unimp).await) } - pub async fn project_image_fetch_by_id(&self, opctx: &OpContext, image_id: Uuid) -> LookupResult { + pub async fn project_image_fetch_by_id( + &self, + opctx: &OpContext, + image_id: Uuid, + ) -> LookupResult { let (.., db_image) = LookupPath::new(opctx, &self.db_datastore) .image_id(image_id) .fetch() .await?; - Ok(db_image) + Ok(db_image) } pub async fn project_delete_image( @@ -304,7 +308,11 @@ impl super::Nexus { Ok(db_disk) } - pub async fn global_image_fetch_by_id(&self, opctx: &OpContext, global_image_id: Uuid) -> LookupResult { + pub async fn global_image_fetch_by_id( + &self, + opctx: &OpContext, + global_image_id: Uuid, + ) -> LookupResult { let (.., db_global_image) = LookupPath::new(opctx, &self.db_datastore) .global_image_id(global_image_id) .fetch() diff --git a/nexus/src/app/instance.rs b/nexus/src/app/instance.rs index 6df4edaf2c..0708c6854f 100644 --- a/nexus/src/app/instance.rs +++ b/nexus/src/app/instance.rs @@ -178,7 +178,11 @@ impl super::Nexus { Ok(db_instance) } - pub async fn instance_fetch_by_id(&self, opctx: &OpContext, instance_id: Uuid) -> LookupResult { + pub async fn instance_fetch_by_id( + &self, + opctx: &OpContext, + instance_id: Uuid, + ) -> LookupResult { let (.., db_instance) = LookupPath::new(opctx, &self.db_datastore) .instance_id(instance_id) .fetch() @@ -768,7 +772,11 @@ impl super::Nexus { Ok(db_interface) } - pub async fn network_interface_fetch_by_id(&self, opctx: &OpContext, interface_id: Uuid) -> LookupResult { + pub async fn network_interface_fetch_by_id( + &self, + opctx: &OpContext, + interface_id: Uuid, + ) -> LookupResult { let (.., db_interface) = LookupPath::new(opctx, &self.db_datastore) .network_interface_id(interface_id) .await?; diff --git a/nexus/src/app/vpc.rs b/nexus/src/app/vpc.rs index 74961e77dc..dea3f6e59d 100644 --- a/nexus/src/app/vpc.rs +++ b/nexus/src/app/vpc.rs @@ -209,10 +209,13 @@ impl super::Nexus { Ok(db_vpc) } - pub async fn vpc_fetch_by_id(&self, opctx: &OpContext, vpc_id: Uuid) -> LookupResult { - let (.., db_vpc) = LookupPath::new(opctx, &self.db_datastore) - .vpc_id(vpc_id) - .await?; + pub async fn vpc_fetch_by_id( + &self, + opctx: &OpContext, + vpc_id: Uuid, + ) -> LookupResult { + let (.., db_vpc) = + LookupPath::new(opctx, &self.db_datastore).vpc_id(vpc_id).await?; Ok(db_vpc) } diff --git a/nexus/src/app/vpc_router.rs b/nexus/src/app/vpc_router.rs index 5f4f35489f..2f4070658c 100644 --- a/nexus/src/app/vpc_router.rs +++ b/nexus/src/app/vpc_router.rs @@ -96,7 +96,11 @@ impl super::Nexus { Ok(db_router) } - pub async fn vpc_router_fetch_by_id(&self, opctx: &OpContext, vpc_router_id: Uuid) -> LookupResult { + pub async fn vpc_router_fetch_by_id( + &self, + opctx: &OpContext, + vpc_router_id: Uuid, + ) -> LookupResult { let (.., db_router) = LookupPath::new(opctx, &self.db_datastore) .vpc_router_id(vpc_router_id) .fetch() @@ -231,7 +235,11 @@ impl super::Nexus { Ok(db_route) } - pub async fn route_fetch_by_id(&self, opctx: &OpContext, route_id: Uuid) -> LookupResult { + pub async fn route_fetch_by_id( + &self, + opctx: &OpContext, + route_id: Uuid, + ) -> LookupResult { let (.., db_route) = LookupPath::new(opctx, &self.db_datastore) .router_route_id(route_id) .fetch() diff --git a/nexus/src/app/vpc_subnet.rs b/nexus/src/app/vpc_subnet.rs index b1def2aba3..02a470c9d0 100644 --- a/nexus/src/app/vpc_subnet.rs +++ b/nexus/src/app/vpc_subnet.rs @@ -218,7 +218,11 @@ impl super::Nexus { Ok(db_vpc) } - pub async fn vpc_subnet_fetch_by_id(&self, opctx: &OpContext, vpc_subnet_id: Uuid) -> LookupResult { + pub async fn vpc_subnet_fetch_by_id( + &self, + opctx: &OpContext, + vpc_subnet_id: Uuid, + ) -> LookupResult { let (.., db_vpc) = LookupPath::new(opctx, &self.db_datastore) .vpc_subnet_id(vpc_subnet_id) .fetch() diff --git a/nexus/src/db/lookup.rs b/nexus/src/db/lookup.rs index 9e3edecda2..9a6e974b58 100644 --- a/nexus/src/db/lookup.rs +++ b/nexus/src/db/lookup.rs @@ -238,13 +238,18 @@ impl<'a> LookupPath<'a> { /// Select a resource of type Snapshot, identified by its id pub fn snapshot_id(self, id: Uuid) -> Snapshot<'a> { - Snapshot { key: SnapshotKey::PrimaryKey(Root { lookup_root: self }, id) } + Snapshot { + key: SnapshotKey::PrimaryKey(Root { lookup_root: self }, id), + } } /// Select a resource of type NetworkInterface, identified by its id pub fn network_interface_id(self, id: Uuid) -> NetworkInterface<'a> { NetworkInterface { - key: NetworkInterfaceKey::PrimaryKey(Root { lookup_root: self }, id), + key: NetworkInterfaceKey::PrimaryKey( + Root { lookup_root: self }, + id, + ), } } diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index c24477c476..a4508a7b38 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -3393,14 +3393,12 @@ async fn sshkeys_delete_key( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } - -/// Path parameters for request by id +/// Path parameters for request by id #[derive(Deserialize, JsonSchema)] struct ByIdPathParams { id: Uuid, } - /// Fetch an organization by id #[endpoint { method = GET, @@ -3571,9 +3569,8 @@ async fn by_id_network_interface_get( let id = &path.id; let handler = async { let opctx = OpContext::for_external_api(&rqctx).await?; - let network_interface = nexus - .network_interface_fetch_by_id(&opctx, id) - .await?; + let network_interface = + nexus.network_interface_fetch_by_id(&opctx, id).await?; Ok(HttpResponseOk(network_interface.into())) }; apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await From 2a1b9e2e192c8bbb4cefc3be3c5a57ef2fa0bfa7 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Fri, 24 Jun 2022 01:48:08 -0400 Subject: [PATCH 05/18] Fix compiler errors --- nexus/src/app/disk.rs | 15 ++++++++------- nexus/src/app/image.rs | 8 ++++---- nexus/src/app/instance.rs | 9 +++++---- nexus/src/app/organization.rs | 4 ++-- nexus/src/app/project.rs | 4 ++-- nexus/src/app/vpc.rs | 8 +++++--- nexus/src/app/vpc_router.rs | 8 ++++---- nexus/src/app/vpc_subnet.rs | 4 ++-- nexus/src/db/model/snapshot.rs | 6 +++--- nexus/src/external_api/http_entrypoints.rs | 8 ++++---- 10 files changed, 39 insertions(+), 35 deletions(-) diff --git a/nexus/src/app/disk.rs b/nexus/src/app/disk.rs index 3c5d772614..21964cb09d 100644 --- a/nexus/src/app/disk.rs +++ b/nexus/src/app/disk.rs @@ -217,10 +217,10 @@ impl super::Nexus { pub async fn disk_fetch_by_id( &self, opctx: &OpContext, - disk_id: Uuid, + disk_id: &Uuid, ) -> LookupResult { let (.., db_disk) = LookupPath::new(opctx, &self.db_datastore) - .disk_id(disk_id) + .disk_id(*disk_id) .fetch() .await?; Ok(db_disk) @@ -401,12 +401,13 @@ impl super::Nexus { pub async fn snapshot_fetch_by_id( &self, opctx: &OpContext, - id: Uuid, + snapshot_id: &Uuid, ) -> LookupResult { - let (.., db_snapshot) = LookupPath::new(opctx, &self.db_datastore) - .snapshot_id(id) - .fetch() - .await?; + let lookup_type = LookupType::ById(*snapshot_id); + let not_found_error = + lookup_type.into_not_found(ResourceType::Snapshot); + let unimp = Unimpl::ProtectedLookup(not_found_error); + Err(self.unimplemented_todo(opctx, unimp).await) } pub async fn project_delete_snapshot( diff --git a/nexus/src/app/image.rs b/nexus/src/app/image.rs index 3a98c20ffa..d583badea2 100644 --- a/nexus/src/app/image.rs +++ b/nexus/src/app/image.rs @@ -74,10 +74,10 @@ impl super::Nexus { pub async fn project_image_fetch_by_id( &self, opctx: &OpContext, - image_id: Uuid, + image_id: &Uuid, ) -> LookupResult { let (.., db_image) = LookupPath::new(opctx, &self.db_datastore) - .image_id(image_id) + .image_id(*image_id) .fetch() .await?; Ok(db_image) @@ -311,10 +311,10 @@ impl super::Nexus { pub async fn global_image_fetch_by_id( &self, opctx: &OpContext, - global_image_id: Uuid, + global_image_id: &Uuid, ) -> LookupResult { let (.., db_global_image) = LookupPath::new(opctx, &self.db_datastore) - .global_image_id(global_image_id) + .global_image_id(*global_image_id) .fetch() .await?; Ok(db_global_image) diff --git a/nexus/src/app/instance.rs b/nexus/src/app/instance.rs index 0708c6854f..5c1f2a533b 100644 --- a/nexus/src/app/instance.rs +++ b/nexus/src/app/instance.rs @@ -181,10 +181,10 @@ impl super::Nexus { pub async fn instance_fetch_by_id( &self, opctx: &OpContext, - instance_id: Uuid, + instance_id: &Uuid, ) -> LookupResult { let (.., db_instance) = LookupPath::new(opctx, &self.db_datastore) - .instance_id(instance_id) + .instance_id(*instance_id) .fetch() .await?; Ok(db_instance) @@ -775,10 +775,11 @@ impl super::Nexus { pub async fn network_interface_fetch_by_id( &self, opctx: &OpContext, - interface_id: Uuid, + interface_id: &Uuid, ) -> LookupResult { let (.., db_interface) = LookupPath::new(opctx, &self.db_datastore) - .network_interface_id(interface_id) + .network_interface_id(*interface_id) + .fetch() .await?; Ok(db_interface) } diff --git a/nexus/src/app/organization.rs b/nexus/src/app/organization.rs index 3d343f682e..d7783d2a7a 100644 --- a/nexus/src/app/organization.rs +++ b/nexus/src/app/organization.rs @@ -45,10 +45,10 @@ impl super::Nexus { pub async fn organization_fetch_by_id( &self, opctx: &OpContext, - organization_id: Uuid, + organization_id: &Uuid, ) -> LookupResult { let (.., db_organization) = LookupPath::new(opctx, &self.db_datastore) - .organization_id(organization_id) + .organization_id(*organization_id) .fetch() .await?; Ok(db_organization) diff --git a/nexus/src/app/project.rs b/nexus/src/app/project.rs index ddad6c0d03..f5cb97255e 100644 --- a/nexus/src/app/project.rs +++ b/nexus/src/app/project.rs @@ -91,10 +91,10 @@ impl super::Nexus { pub async fn project_fetch_by_id( &self, opctx: &OpContext, - project_id: Uuid, + project_id: &Uuid, ) -> LookupResult { let (.., db_project) = LookupPath::new(opctx, &self.db_datastore) - .project_id(project_id) + .project_id(*project_id) .fetch() .await?; Ok(db_project) diff --git a/nexus/src/app/vpc.rs b/nexus/src/app/vpc.rs index dea3f6e59d..af6017ccfe 100644 --- a/nexus/src/app/vpc.rs +++ b/nexus/src/app/vpc.rs @@ -212,10 +212,12 @@ impl super::Nexus { pub async fn vpc_fetch_by_id( &self, opctx: &OpContext, - vpc_id: Uuid, + vpc_id: &Uuid, ) -> LookupResult { - let (.., db_vpc) = - LookupPath::new(opctx, &self.db_datastore).vpc_id(vpc_id).await?; + let (.., db_vpc) = LookupPath::new(opctx, &self.db_datastore) + .vpc_id(*vpc_id) + .fetch() + .await?; Ok(db_vpc) } diff --git a/nexus/src/app/vpc_router.rs b/nexus/src/app/vpc_router.rs index 2f4070658c..9af86c6ed2 100644 --- a/nexus/src/app/vpc_router.rs +++ b/nexus/src/app/vpc_router.rs @@ -99,10 +99,10 @@ impl super::Nexus { pub async fn vpc_router_fetch_by_id( &self, opctx: &OpContext, - vpc_router_id: Uuid, + vpc_router_id: &Uuid, ) -> LookupResult { let (.., db_router) = LookupPath::new(opctx, &self.db_datastore) - .vpc_router_id(vpc_router_id) + .vpc_router_id(*vpc_router_id) .fetch() .await?; Ok(db_router) @@ -238,10 +238,10 @@ impl super::Nexus { pub async fn route_fetch_by_id( &self, opctx: &OpContext, - route_id: Uuid, + route_id: &Uuid, ) -> LookupResult { let (.., db_route) = LookupPath::new(opctx, &self.db_datastore) - .router_route_id(route_id) + .router_route_id(*route_id) .fetch() .await?; Ok(db_route) diff --git a/nexus/src/app/vpc_subnet.rs b/nexus/src/app/vpc_subnet.rs index 02a470c9d0..26f9089c3e 100644 --- a/nexus/src/app/vpc_subnet.rs +++ b/nexus/src/app/vpc_subnet.rs @@ -221,10 +221,10 @@ impl super::Nexus { pub async fn vpc_subnet_fetch_by_id( &self, opctx: &OpContext, - vpc_subnet_id: Uuid, + vpc_subnet_id: &Uuid, ) -> LookupResult { let (.., db_vpc) = LookupPath::new(opctx, &self.db_datastore) - .vpc_subnet_id(vpc_subnet_id) + .vpc_subnet_id(*vpc_subnet_id) .fetch() .await?; Ok(db_vpc) diff --git a/nexus/src/db/model/snapshot.rs b/nexus/src/db/model/snapshot.rs index 08b3ab44ee..6ea9b934de 100644 --- a/nexus/src/db/model/snapshot.rs +++ b/nexus/src/db/model/snapshot.rs @@ -25,9 +25,9 @@ pub struct Snapshot { #[diesel(embed)] identity: SnapshotIdentity, - project_id: Uuid, - disk_id: Uuid, - volume_id: Uuid, + pub project_id: Uuid, + pub disk_id: Uuid, + pub volume_id: Uuid, #[diesel(column_name = size_bytes)] pub size: ByteCount, diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index a4508a7b38..7d5ddcef91 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -3503,7 +3503,7 @@ async fn by_id_image_get( let id = &path.id; let handler = async { let opctx = OpContext::for_external_api(&rqctx).await?; - let image = nexus.image_fetch_by_id(&opctx, id).await?; + let image = nexus.project_image_fetch_by_id(&opctx, id).await?; Ok(HttpResponseOk(image.into())) }; apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await @@ -3607,14 +3607,14 @@ async fn by_id_vpc_get( async fn by_id_subnet_get( rqctx: Arc>>, path_params: Path, -) -> Result, HttpError> { +) -> Result, HttpError> { let apictx = rqctx.context(); let nexus = &apictx.nexus; let path = path_params.into_inner(); let id = &path.id; let handler = async { let opctx = OpContext::for_external_api(&rqctx).await?; - let subnet = nexus.subnet_fetch_by_id(&opctx, id).await?; + let subnet = nexus.vpc_subnet_fetch_by_id(&opctx, id).await?; Ok(HttpResponseOk(subnet.into())) }; apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await @@ -3651,7 +3651,7 @@ async fn by_id_router_get( async fn by_id_route_get( rqctx: Arc>>, path_params: Path, -) -> Result, HttpError> { +) -> Result, HttpError> { let apictx = rqctx.context(); let nexus = &apictx.nexus; let path = path_params.into_inner(); From eaac6396e01f3934f436643b35b0822411c19989 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Fri, 24 Jun 2022 03:09:32 -0400 Subject: [PATCH 06/18] Update config --- nexus/tests/output/nexus_tags.txt | 12 + openapi/nexus.json | 468 ++++++++++++++++++++++++++++++ 2 files changed, 480 insertions(+) diff --git a/nexus/tests/output/nexus_tags.txt b/nexus/tests/output/nexus_tags.txt index b470210dda..17b71821f5 100644 --- a/nexus/tests/output/nexus_tags.txt +++ b/nexus/tests/output/nexus_tags.txt @@ -1,5 +1,6 @@ API operations found with tag "disks" OPERATION ID URL PATH +by_id_disk_get /by_id/disks/{id} project_disks_delete_disk /organizations/{organization_name}/projects/{project_name}/disks/{disk_name} project_disks_get /organizations/{organization_name}/projects/{project_name}/disks project_disks_get_disk /organizations/{organization_name}/projects/{project_name}/disks/{disk_name} @@ -18,6 +19,7 @@ spoof_login /login API operations found with tag "images" OPERATION ID URL PATH +by_id_image_get /by_id/images/{id} project_images_delete_image /organizations/{organization_name}/projects/{project_name}/images/{image_name} project_images_get /organizations/{organization_name}/projects/{project_name}/images project_images_get_image /organizations/{organization_name}/projects/{project_name}/images/{image_name} @@ -25,6 +27,7 @@ project_images_post /organizations/{organization_name}/proj API operations found with tag "images:global" OPERATION ID URL PATH +by_id_global_image_get /by_id/global-images/{id} images_delete_image /images/{image_name} images_get /images images_get_image /images/{image_name} @@ -32,6 +35,8 @@ images_post /images API operations found with tag "instances" OPERATION ID URL PATH +by_id_instance_get /by_id/instances/{id} +by_id_network_interface_get /by_id/network-interfaces/{id} instance_disks_attach /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/disks/attach instance_disks_detach /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/disks/detach instance_disks_get /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/disks @@ -61,6 +66,7 @@ timeseries_schema_get /timeseries/schema API operations found with tag "organizations" OPERATION ID URL PATH +by_id_organization_get /by_id/organizations/{id} organization_get_policy /organizations/{organization_name}/policy organization_put_policy /organizations/{organization_name}/policy organizations_delete_organization /organizations/{organization_name} @@ -76,6 +82,7 @@ policy_put /policy API operations found with tag "projects" OPERATION ID URL PATH +by_id_project_get /by_id/projects/{id} organization_projects_delete_project /organizations/{organization_name}/projects/{project_name} organization_projects_get /organizations/{organization_name}/projects organization_projects_get_project /organizations/{organization_name}/projects/{project_name} @@ -96,6 +103,7 @@ roles_get_role /roles/{role_name} API operations found with tag "routers" OPERATION ID URL PATH +by_id_router_get /by_id/router/{id} vpc_routers_delete_router /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name} vpc_routers_get /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers vpc_routers_get_router /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name} @@ -104,6 +112,7 @@ vpc_routers_put_router /organizations/{organization_name}/proj API operations found with tag "routes" OPERATION ID URL PATH +by_id_route_get /by_id/route/{id} routers_routes_delete_route /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name}/routes/{route_name} routers_routes_get /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name}/routes routers_routes_get_route /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name}/routes/{route_name} @@ -134,6 +143,7 @@ hardware_sleds_get_sled /hardware/sleds/{sled_id} API operations found with tag "snapshots" OPERATION ID URL PATH +by_id_snapshot_get /by_id/snapshots/{id} project_snapshots_delete_snapshot /organizations/{organization_name}/projects/{project_name}/snapshots/{snapshot_name} project_snapshots_get /organizations/{organization_name}/projects/{project_name}/snapshots project_snapshots_get_snapshot /organizations/{organization_name}/projects/{project_name}/snapshots/{snapshot_name} @@ -148,6 +158,7 @@ sshkeys_post /session/me/sshkeys API operations found with tag "subnets" OPERATION ID URL PATH +by_id_subnet_get /by_id/subnets/{id} subnet_network_interfaces_get /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets/{subnet_name}/network-interfaces vpc_subnets_delete_subnet /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets/{subnet_name} vpc_subnets_get /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets @@ -166,6 +177,7 @@ users_get_user /users/{user_name} API operations found with tag "vpcs" OPERATION ID URL PATH +by_id_vpc_get /by_id/vpcs/{id} project_vpcs_delete_vpc /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name} project_vpcs_get /organizations/{organization_name}/projects/{project_name}/vpcs project_vpcs_get_vpc /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name} diff --git a/openapi/nexus.json b/openapi/nexus.json index 3a9c4e7106..916930f405 100644 --- a/openapi/nexus.json +++ b/openapi/nexus.json @@ -10,6 +10,474 @@ "version": "0.0.1" }, "paths": { + "/by_id/disks/{id}": { + "get": { + "tags": [ + "disks" + ], + "summary": "Fetch a disk by id", + "operationId": "by_id_disk_get", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Disk" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/by_id/global-images/{id}": { + "get": { + "tags": [ + "images:global" + ], + "summary": "Fetch a global image by id", + "operationId": "by_id_global_image_get", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GlobalImage" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/by_id/images/{id}": { + "get": { + "tags": [ + "images" + ], + "summary": "Fetch an image by id", + "operationId": "by_id_image_get", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Image" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/by_id/instances/{id}": { + "get": { + "tags": [ + "instances" + ], + "summary": "Fetch an instance by id", + "operationId": "by_id_instance_get", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Instance" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/by_id/network-interfaces/{id}": { + "get": { + "tags": [ + "instances" + ], + "summary": "Fetch a network interface by id", + "operationId": "by_id_network_interface_get", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NetworkInterface" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/by_id/organizations/{id}": { + "get": { + "tags": [ + "organizations" + ], + "summary": "Fetch an organization by id", + "operationId": "by_id_organization_get", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Organization" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/by_id/projects/{id}": { + "get": { + "tags": [ + "projects" + ], + "summary": "Fetch a project by id", + "operationId": "by_id_project_get", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Project" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/by_id/route/{id}": { + "get": { + "tags": [ + "routes" + ], + "summary": "Fetch a vpc router route by id", + "operationId": "by_id_route_get", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RouterRoute" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/by_id/router/{id}": { + "get": { + "tags": [ + "routers" + ], + "summary": "Fetch a vpc router by id", + "operationId": "by_id_router_get", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VpcRouter" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/by_id/snapshots/{id}": { + "get": { + "tags": [ + "snapshots" + ], + "summary": "Fetch a snapshot by id", + "operationId": "by_id_snapshot_get", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Snapshot" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/by_id/subnets/{id}": { + "get": { + "tags": [ + "subnets" + ], + "summary": "Fetch a vpc subnet by id", + "operationId": "by_id_subnet_get", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VpcSubnet" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/by_id/vpcs/{id}": { + "get": { + "tags": [ + "vpcs" + ], + "summary": "Fetch a vpc by id", + "operationId": "by_id_vpc_get", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Vpc" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, "/hardware/racks": { "get": { "tags": [ From 6632e1922b9457ee9e38c53db60ce793e33573b9 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Fri, 1 Jul 2022 10:58:18 -0400 Subject: [PATCH 07/18] Reverse by_id/resource name order --- nexus/src/external_api/http_entrypoints.rs | 48 +++++++++++----------- nexus/tests/output/nexus_tags.txt | 24 +++++------ openapi/nexus.json | 24 +++++------ 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index e267125063..090fba8d5a 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -226,18 +226,18 @@ pub fn external_api() -> NexusApiDescription { api.register(console_api::login)?; api.register(console_api::consume_credentials)?; - api.register(by_id_organization_get)?; - api.register(by_id_project_get)?; - api.register(by_id_instance_get)?; - api.register(by_id_disk_get)?; - api.register(by_id_image_get)?; - api.register(by_id_global_image_get)?; - api.register(by_id_snapshot_get)?; - api.register(by_id_vpc_get)?; - api.register(by_id_subnet_get)?; - api.register(by_id_router_get)?; - api.register(by_id_route_get)?; - api.register(by_id_network_interface_get)?; + api.register(organization_get_by_id)?; + api.register(project_get_by_id)?; + api.register(instance_get_by_id)?; + api.register(disk_get_by_id)?; + api.register(image_get_by_id)?; + api.register(global_image_get_by_id)?; + api.register(snapshot_get_by_id)?; + api.register(vpc_get_by_id)?; + api.register(subnet_get_by_id)?; + api.register(router_get_by_id)?; + api.register(route_get_by_id)?; + api.register(network_interface_get_by_id)?; api.register(device_auth::device_auth_request)?; api.register(device_auth::device_auth_verify)?; @@ -3777,7 +3777,7 @@ struct ByIdPathParams { path = "/by_id/organizations/{id}", tags = ["organizations"], }] -async fn by_id_organization_get( +async fn organization_get_by_id( rqctx: Arc>>, path_params: Path, ) -> Result, HttpError> { @@ -3799,7 +3799,7 @@ async fn by_id_organization_get( path = "/by_id/projects/{id}", tags = ["projects"], }] -async fn by_id_project_get( +async fn project_get_by_id( rqctx: Arc>>, path_params: Path, ) -> Result, HttpError> { @@ -3821,7 +3821,7 @@ async fn by_id_project_get( path = "/by_id/instances/{id}", tags = ["instances"], }] -async fn by_id_instance_get( +async fn instance_get_by_id( rqctx: Arc>>, path_params: Path, ) -> Result, HttpError> { @@ -3843,7 +3843,7 @@ async fn by_id_instance_get( path = "/by_id/disks/{id}", tags = ["disks"], }] -async fn by_id_disk_get( +async fn disk_get_by_id( rqctx: Arc>>, path_params: Path, ) -> Result, HttpError> { @@ -3865,7 +3865,7 @@ async fn by_id_disk_get( path = "/by_id/images/{id}", tags = ["images"], }] -async fn by_id_image_get( +async fn image_get_by_id( rqctx: Arc>>, path_params: Path, ) -> Result, HttpError> { @@ -3887,7 +3887,7 @@ async fn by_id_image_get( path = "/by_id/global-images/{id}", tags = ["images:global"], }] -async fn by_id_global_image_get( +async fn global_image_get_by_id( rqctx: Arc>>, path_params: Path, ) -> Result, HttpError> { @@ -3909,7 +3909,7 @@ async fn by_id_global_image_get( path = "/by_id/snapshots/{id}", tags = ["snapshots"], }] -async fn by_id_snapshot_get( +async fn snapshot_get_by_id( rqctx: Arc>>, path_params: Path, ) -> Result, HttpError> { @@ -3931,7 +3931,7 @@ async fn by_id_snapshot_get( path = "/by_id/network-interfaces/{id}", tags = ["instances"], }] -async fn by_id_network_interface_get( +async fn network_interface_get_by_id( rqctx: Arc>>, path_params: Path, ) -> Result, HttpError> { @@ -3954,7 +3954,7 @@ async fn by_id_network_interface_get( path = "/by_id/vpcs/{id}", tags = ["vpcs"], }] -async fn by_id_vpc_get( +async fn vpc_get_by_id( rqctx: Arc>>, path_params: Path, ) -> Result, HttpError> { @@ -3976,7 +3976,7 @@ async fn by_id_vpc_get( path = "/by_id/subnets/{id}", tags = ["subnets"], }] -async fn by_id_subnet_get( +async fn subnet_get_by_id( rqctx: Arc>>, path_params: Path, ) -> Result, HttpError> { @@ -3998,7 +3998,7 @@ async fn by_id_subnet_get( path = "/by_id/router/{id}", tags = ["routers"], }] -async fn by_id_router_get( +async fn router_get_by_id( rqctx: Arc>>, path_params: Path, ) -> Result, HttpError> { @@ -4020,7 +4020,7 @@ async fn by_id_router_get( path = "/by_id/route/{id}", tags = ["routes"] }] -async fn by_id_route_get( +async fn route_get_by_id( rqctx: Arc>>, path_params: Path, ) -> Result, HttpError> { diff --git a/nexus/tests/output/nexus_tags.txt b/nexus/tests/output/nexus_tags.txt index 1be1d61b34..e116d09d6c 100644 --- a/nexus/tests/output/nexus_tags.txt +++ b/nexus/tests/output/nexus_tags.txt @@ -1,6 +1,6 @@ API operations found with tag "disks" OPERATION ID URL PATH -by_id_disk_get /by_id/disks/{id} +disk_get_by_id /by_id/disks/{id} project_disks_delete_disk /organizations/{organization_name}/projects/{project_name}/disks/{disk_name} project_disks_get /organizations/{organization_name}/projects/{project_name}/disks project_disks_get_disk /organizations/{organization_name}/projects/{project_name}/disks/{disk_name} @@ -23,7 +23,7 @@ spoof_login /login API operations found with tag "images" OPERATION ID URL PATH -by_id_image_get /by_id/images/{id} +image_get_by_id /by_id/images/{id} project_images_delete_image /organizations/{organization_name}/projects/{project_name}/images/{image_name} project_images_get /organizations/{organization_name}/projects/{project_name}/images project_images_get_image /organizations/{organization_name}/projects/{project_name}/images/{image_name} @@ -31,7 +31,7 @@ project_images_post /organizations/{organization_name}/proj API operations found with tag "images:global" OPERATION ID URL PATH -by_id_global_image_get /by_id/global-images/{id} +global_image_get_by_id /by_id/global-images/{id} images_delete_image /images/{image_name} images_get /images images_get_image /images/{image_name} @@ -39,16 +39,16 @@ images_post /images API operations found with tag "instances" OPERATION ID URL PATH -by_id_instance_get /by_id/instances/{id} -by_id_network_interface_get /by_id/network-interfaces/{id} instance_disks_attach /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/disks/attach instance_disks_detach /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/disks/detach instance_disks_get /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/disks +instance_get_by_id /by_id/instances/{id} instance_network_interfaces_delete_interface /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/network-interfaces/{interface_name} instance_network_interfaces_get /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/network-interfaces instance_network_interfaces_get_interface /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/network-interfaces/{interface_name} instance_network_interfaces_post /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/network-interfaces instance_network_interfaces_put_interface /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/network-interfaces/{interface_name} +network_interface_get_by_id /by_id/network-interfaces/{id} project_instances_delete_instance /organizations/{organization_name}/projects/{project_name}/instances/{instance_name} project_instances_get /organizations/{organization_name}/projects/{project_name}/instances project_instances_get_instance /organizations/{organization_name}/projects/{project_name}/instances/{instance_name} @@ -81,7 +81,7 @@ timeseries_schema_get /timeseries/schema API operations found with tag "organizations" OPERATION ID URL PATH -by_id_organization_get /by_id/organizations/{id} +organization_get_by_id /by_id/organizations/{id} organization_get_policy /organizations/{organization_name}/policy organization_put_policy /organizations/{organization_name}/policy organizations_delete_organization /organizations/{organization_name} @@ -97,7 +97,6 @@ policy_put /policy API operations found with tag "projects" OPERATION ID URL PATH -by_id_project_get /by_id/projects/{id} organization_projects_delete_project /organizations/{organization_name}/projects/{project_name} organization_projects_get /organizations/{organization_name}/projects organization_projects_get_project /organizations/{organization_name}/projects/{project_name} @@ -105,6 +104,7 @@ organization_projects_get_project_policy /organizations/{organization_name}/proj organization_projects_post /organizations/{organization_name}/projects organization_projects_put_project /organizations/{organization_name}/projects/{project_name} organization_projects_put_project_policy /organizations/{organization_name}/projects/{project_name}/policy +project_get_by_id /by_id/projects/{id} API operations found with tag "racks" OPERATION ID URL PATH @@ -118,7 +118,7 @@ roles_get_role /roles/{role_name} API operations found with tag "routers" OPERATION ID URL PATH -by_id_router_get /by_id/router/{id} +router_get_by_id /by_id/router/{id} vpc_routers_delete_router /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name} vpc_routers_get /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers vpc_routers_get_router /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name} @@ -127,7 +127,7 @@ vpc_routers_put_router /organizations/{organization_name}/proj API operations found with tag "routes" OPERATION ID URL PATH -by_id_route_get /by_id/route/{id} +route_get_by_id /by_id/route/{id} routers_routes_delete_route /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name}/routes/{route_name} routers_routes_get /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name}/routes routers_routes_get_route /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name}/routes/{route_name} @@ -159,11 +159,11 @@ hardware_sleds_get_sled /hardware/sleds/{sled_id} API operations found with tag "snapshots" OPERATION ID URL PATH -by_id_snapshot_get /by_id/snapshots/{id} project_snapshots_delete_snapshot /organizations/{organization_name}/projects/{project_name}/snapshots/{snapshot_name} project_snapshots_get /organizations/{organization_name}/projects/{project_name}/snapshots project_snapshots_get_snapshot /organizations/{organization_name}/projects/{project_name}/snapshots/{snapshot_name} project_snapshots_post /organizations/{organization_name}/projects/{project_name}/snapshots +snapshot_get_by_id /by_id/snapshots/{id} API operations found with tag "sshkeys" OPERATION ID URL PATH @@ -174,7 +174,7 @@ sshkeys_post /session/me/sshkeys API operations found with tag "subnets" OPERATION ID URL PATH -by_id_subnet_get /by_id/subnets/{id} +subnet_get_by_id /by_id/subnets/{id} subnet_network_interfaces_get /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets/{subnet_name}/network-interfaces vpc_subnets_delete_subnet /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets/{subnet_name} vpc_subnets_get /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets @@ -193,10 +193,10 @@ updates_refresh /updates/refresh API operations found with tag "vpcs" OPERATION ID URL PATH -by_id_vpc_get /by_id/vpcs/{id} project_vpcs_delete_vpc /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name} project_vpcs_get /organizations/{organization_name}/projects/{project_name}/vpcs project_vpcs_get_vpc /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name} project_vpcs_post /organizations/{organization_name}/projects/{project_name}/vpcs project_vpcs_put_vpc /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name} +vpc_get_by_id /by_id/vpcs/{id} diff --git a/openapi/nexus.json b/openapi/nexus.json index 5087ff06dc..9ff3cbad31 100644 --- a/openapi/nexus.json +++ b/openapi/nexus.json @@ -16,7 +16,7 @@ "disks" ], "summary": "Fetch a disk by id", - "operationId": "by_id_disk_get", + "operationId": "disk_get_by_id", "parameters": [ { "in": "path", @@ -55,7 +55,7 @@ "images:global" ], "summary": "Fetch a global image by id", - "operationId": "by_id_global_image_get", + "operationId": "global_image_get_by_id", "parameters": [ { "in": "path", @@ -94,7 +94,7 @@ "images" ], "summary": "Fetch an image by id", - "operationId": "by_id_image_get", + "operationId": "image_get_by_id", "parameters": [ { "in": "path", @@ -133,7 +133,7 @@ "instances" ], "summary": "Fetch an instance by id", - "operationId": "by_id_instance_get", + "operationId": "instance_get_by_id", "parameters": [ { "in": "path", @@ -172,7 +172,7 @@ "instances" ], "summary": "Fetch a network interface by id", - "operationId": "by_id_network_interface_get", + "operationId": "network_interface_get_by_id", "parameters": [ { "in": "path", @@ -211,7 +211,7 @@ "organizations" ], "summary": "Fetch an organization by id", - "operationId": "by_id_organization_get", + "operationId": "organization_get_by_id", "parameters": [ { "in": "path", @@ -250,7 +250,7 @@ "projects" ], "summary": "Fetch a project by id", - "operationId": "by_id_project_get", + "operationId": "project_get_by_id", "parameters": [ { "in": "path", @@ -289,7 +289,7 @@ "routes" ], "summary": "Fetch a vpc router route by id", - "operationId": "by_id_route_get", + "operationId": "route_get_by_id", "parameters": [ { "in": "path", @@ -328,7 +328,7 @@ "routers" ], "summary": "Fetch a vpc router by id", - "operationId": "by_id_router_get", + "operationId": "router_get_by_id", "parameters": [ { "in": "path", @@ -367,7 +367,7 @@ "snapshots" ], "summary": "Fetch a snapshot by id", - "operationId": "by_id_snapshot_get", + "operationId": "snapshot_get_by_id", "parameters": [ { "in": "path", @@ -406,7 +406,7 @@ "subnets" ], "summary": "Fetch a vpc subnet by id", - "operationId": "by_id_subnet_get", + "operationId": "subnet_get_by_id", "parameters": [ { "in": "path", @@ -445,7 +445,7 @@ "vpcs" ], "summary": "Fetch a vpc by id", - "operationId": "by_id_vpc_get", + "operationId": "vpc_get_by_id", "parameters": [ { "in": "path", From e42bfaf18a258980c80e4ea45e1b7a919180524c Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Wed, 6 Jul 2022 19:20:09 -0400 Subject: [PATCH 08/18] reorganize imports --- nexus/src/external_api/http_entrypoints.rs | 25 +++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index ea3fe130a4..1e334db157 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -84,6 +84,7 @@ pub fn external_api() -> NexusApiDescription { api.register(organization_list)?; api.register(organization_create)?; api.register(organization_view)?; + api.register(organization_view_by_id)?; api.register(organization_delete)?; api.register(organization_update)?; api.register(organization_policy_view)?; @@ -92,6 +93,7 @@ pub fn external_api() -> NexusApiDescription { api.register(project_list)?; api.register(project_create)?; api.register(project_view)?; + api.register(project_view_by_id)?; api.register(project_delete)?; api.register(project_update)?; api.register(project_policy_view)?; @@ -110,11 +112,13 @@ pub fn external_api() -> NexusApiDescription { api.register(disk_list)?; api.register(disk_create)?; api.register(disk_view)?; + api.register(disk_view_by_id)?; api.register(disk_delete)?; api.register(instance_list)?; api.register(instance_create)?; api.register(instance_view)?; + api.register(instance_view_by_id)?; api.register(instance_delete)?; api.register(instance_migrate)?; api.register(instance_reboot)?; @@ -126,6 +130,7 @@ pub fn external_api() -> NexusApiDescription { api.register(image_list)?; api.register(image_create)?; api.register(image_view)?; + api.register(image_view_by_id)?; api.register(image_delete)?; api.register(instance_disk_list)?; @@ -135,16 +140,19 @@ pub fn external_api() -> NexusApiDescription { api.register(snapshot_list)?; api.register(snapshot_create)?; api.register(snapshot_view)?; + api.register(snapshot_view_by_id)?; api.register(snapshot_delete)?; api.register(vpc_list)?; api.register(vpc_create)?; api.register(vpc_view)?; + api.register(vpc_view_by_id)?; api.register(vpc_update)?; api.register(vpc_delete)?; api.register(vpc_subnet_list)?; api.register(vpc_subnet_view)?; + api.register(vpc_subnet_view_by_id)?; api.register(vpc_subnet_create)?; api.register(vpc_subnet_delete)?; api.register(vpc_subnet_update)?; @@ -153,17 +161,20 @@ pub fn external_api() -> NexusApiDescription { api.register(instance_network_interface_create)?; api.register(instance_network_interface_list)?; api.register(instance_network_interface_view)?; + api.register(instance_network_interface_view_by_id)?; api.register(instance_network_interface_update)?; api.register(instance_network_interface_delete)?; api.register(vpc_router_list)?; api.register(vpc_router_view)?; + api.register(vpc_router_view_by_id)?; api.register(vpc_router_create)?; api.register(vpc_router_delete)?; api.register(vpc_router_update)?; api.register(vpc_router_route_list)?; api.register(vpc_router_route_view)?; + api.register(vpc_router_route_view_by_id)?; api.register(vpc_router_route_create)?; api.register(vpc_router_route_delete)?; api.register(vpc_router_route_update)?; @@ -207,6 +218,7 @@ pub fn external_api() -> NexusApiDescription { api.register(image_global_list)?; api.register(image_global_create)?; api.register(image_global_view)?; + api.register(image_global_view_by_id)?; api.register(image_global_delete)?; api.register(updates_refresh)?; @@ -224,19 +236,6 @@ pub fn external_api() -> NexusApiDescription { api.register(console_api::login)?; api.register(console_api::consume_credentials)?; - api.register(organization_get_by_id)?; - api.register(project_get_by_id)?; - api.register(instance_get_by_id)?; - api.register(disk_get_by_id)?; - api.register(image_get_by_id)?; - api.register(global_image_get_by_id)?; - api.register(snapshot_get_by_id)?; - api.register(vpc_get_by_id)?; - api.register(subnet_get_by_id)?; - api.register(router_get_by_id)?; - api.register(route_get_by_id)?; - api.register(network_interface_get_by_id)?; - api.register(device_auth::device_auth_request)?; api.register(device_auth::device_auth_verify)?; api.register(device_auth::device_auth_success)?; From b59d183893f936c5c60d1a1c622dfd415c0327ea Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Wed, 6 Jul 2022 19:40:52 -0400 Subject: [PATCH 09/18] Reorganize id lookup implementation code --- nexus/src/external_api/http_entrypoints.rs | 546 +++++++++++---------- 1 file changed, 274 insertions(+), 272 deletions(-) diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index 1e334db157..668ed78f1f 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -310,6 +310,13 @@ async fn policy_view( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } + +/// Path parameters for `/by_id/` endpoints +#[derive(Deserialize, JsonSchema)] +struct ByIdPathParams { + id: Uuid, +} + /// Update the top-level IAM policy #[endpoint { method = PUT, @@ -705,6 +712,28 @@ async fn organization_view( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// Get an organization by id +#[endpoint { + method = GET, + path = "/by_id/organizations/{id}", + tags = ["organizations"], +}] +async fn organization_view_by_id( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let organization = nexus.organization_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(organization.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + /// Delete a specific organization. #[endpoint { method = DELETE, @@ -938,6 +967,28 @@ async fn project_view( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// Get a project by id +#[endpoint { + method = GET, + path = "/by_id/projects/{id}", + tags = ["projects"], +}] +async fn project_view_by_id( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let project = nexus.project_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(project.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + /// Delete a specific project. #[endpoint { method = DELETE, @@ -1375,7 +1426,7 @@ struct DiskPathParam { disk_name: Name, } -/// Fetch a single disk in a project. +/// Get a single disk in a project. #[endpoint { method = GET, path = "/organizations/{organization_name}/projects/{project_name}/disks/{disk_name}", @@ -1401,6 +1452,28 @@ async fn disk_view( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// Get a disk by id +#[endpoint { + method = GET, + path = "/by_id/disks/{id}", + tags = ["disks"], +}] +async fn disk_view_by_id( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let disk = nexus.disk_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(disk.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + /// Delete a disk from a project. #[endpoint { method = DELETE, @@ -1552,6 +1625,28 @@ async fn instance_view( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// Get an instance by id. +#[endpoint { + method = GET, + path = "/by_id/instances/{id}", + tags = ["instances"], +}] +async fn instance_view_by_id( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let instance = nexus.instance_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(instance.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + /// Delete an instance from a project. #[endpoint { method = DELETE, @@ -1944,6 +2039,28 @@ async fn image_global_view( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// Get a global image by id. +#[endpoint { + method = GET, + path = "/by_id/global-images/{id}", + tags = ["images:global"], +}] +async fn image_global_view_by_id( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let image = nexus.global_image_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(image.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + /// Delete a global image. /// /// Permanently delete a global image. This operation cannot be undone. Any @@ -2088,6 +2205,29 @@ async fn image_view( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// Fetch an image by id +#[endpoint { + method = GET, + path = "/by_id/images/{id}", + tags = ["images"], +}] +async fn image_view_by_id( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let image = nexus.project_image_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(image.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + + /// Delete an image /// /// Permanently delete an image from a project. This operation cannot be undone. @@ -2281,6 +2421,29 @@ async fn instance_network_interface_view( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// Get an instance's network interface by id. +#[endpoint { + method = GET, + path = "/by_id/network-interfaces/{id}", + tags = ["instances"], +}] +async fn instance_network_interface_view_by_id( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let network_interface = + nexus.network_interface_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(network_interface.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + /// Update information about an instance's network interface #[endpoint { method = PUT, @@ -2430,6 +2593,28 @@ async fn snapshot_view( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// Get a snapshot by id. +#[endpoint { + method = GET, + path = "/by_id/snapshots/{id}", + tags = ["snapshots"], +}] +async fn snapshot_view_by_id( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let snapshot = nexus.snapshot_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(snapshot.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + /// Delete a snapshot from a project. #[endpoint { method = DELETE, @@ -2538,6 +2723,28 @@ async fn vpc_view( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// Get a VPC by id. +#[endpoint { + method = GET, + path = "/by_id/vpcs/{id}", + tags = ["vpcs"], +}] +async fn vpc_view_by_id( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let vpc = nexus.vpc_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(vpc.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + /// Create a VPC in a project. #[endpoint { method = POST, @@ -2708,6 +2915,28 @@ async fn vpc_subnet_view( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// Get a VPC subnet by id. +#[endpoint { + method = GET, + path = "/by_id/subnets/{id}", + tags = ["subnets"], +}] +async fn vpc_subnet_view_by_id( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let subnet = nexus.vpc_subnet_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(subnet.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + /// Create a subnet in a VPC. #[endpoint { method = POST, @@ -2987,6 +3216,28 @@ async fn vpc_router_view( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// Get a VPC Router by id +#[endpoint { + method = GET, + path = "/by_id/router/{id}", + tags = ["routers"], +}] +async fn vpc_router_view_by_id( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let router = nexus.vpc_router_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(router.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + /// Create a VPC Router #[endpoint { method = POST, @@ -3160,6 +3411,28 @@ async fn vpc_router_route_view( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// Get a vpc router route by id +#[endpoint { + method = GET, + path = "/by_id/route/{id}", + tags = ["routes"] +}] +async fn vpc_router_route_view_by_id( + rqctx: Arc>>, + path_params: Path, +) -> Result, HttpError> { + let apictx = rqctx.context(); + let nexus = &apictx.nexus; + let path = path_params.into_inner(); + let id = &path.id; + let handler = async { + let opctx = OpContext::for_external_api(&rqctx).await?; + let route = nexus.route_fetch_by_id(&opctx, id).await?; + Ok(HttpResponseOk(route.into())) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + /// Create a VPC Router #[endpoint { method = POST, @@ -3765,274 +4038,3 @@ async fn session_sshkey_delete( }; apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } - -/// Path parameters for request by id -#[derive(Deserialize, JsonSchema)] -struct ByIdPathParams { - id: Uuid, -} - -/// Fetch an organization by id -#[endpoint { - method = GET, - path = "/by_id/organizations/{id}", - tags = ["organizations"], -}] -async fn organization_get_by_id( - rqctx: Arc>>, - path_params: Path, -) -> Result, HttpError> { - let apictx = rqctx.context(); - let nexus = &apictx.nexus; - let path = path_params.into_inner(); - let id = &path.id; - let handler = async { - let opctx = OpContext::for_external_api(&rqctx).await?; - let organization = nexus.organization_fetch_by_id(&opctx, id).await?; - Ok(HttpResponseOk(organization.into())) - }; - apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await -} - -/// Fetch a project by id -#[endpoint { - method = GET, - path = "/by_id/projects/{id}", - tags = ["projects"], -}] -async fn project_get_by_id( - rqctx: Arc>>, - path_params: Path, -) -> Result, HttpError> { - let apictx = rqctx.context(); - let nexus = &apictx.nexus; - let path = path_params.into_inner(); - let id = &path.id; - let handler = async { - let opctx = OpContext::for_external_api(&rqctx).await?; - let project = nexus.project_fetch_by_id(&opctx, id).await?; - Ok(HttpResponseOk(project.into())) - }; - apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await -} - -/// Fetch an instance by id -#[endpoint { - method = GET, - path = "/by_id/instances/{id}", - tags = ["instances"], -}] -async fn instance_get_by_id( - rqctx: Arc>>, - path_params: Path, -) -> Result, HttpError> { - let apictx = rqctx.context(); - let nexus = &apictx.nexus; - let path = path_params.into_inner(); - let id = &path.id; - let handler = async { - let opctx = OpContext::for_external_api(&rqctx).await?; - let instance = nexus.instance_fetch_by_id(&opctx, id).await?; - Ok(HttpResponseOk(instance.into())) - }; - apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await -} - -/// Fetch a disk by id -#[endpoint { - method = GET, - path = "/by_id/disks/{id}", - tags = ["disks"], -}] -async fn disk_get_by_id( - rqctx: Arc>>, - path_params: Path, -) -> Result, HttpError> { - let apictx = rqctx.context(); - let nexus = &apictx.nexus; - let path = path_params.into_inner(); - let id = &path.id; - let handler = async { - let opctx = OpContext::for_external_api(&rqctx).await?; - let disk = nexus.disk_fetch_by_id(&opctx, id).await?; - Ok(HttpResponseOk(disk.into())) - }; - apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await -} - -/// Fetch an image by id -#[endpoint { - method = GET, - path = "/by_id/images/{id}", - tags = ["images"], -}] -async fn image_get_by_id( - rqctx: Arc>>, - path_params: Path, -) -> Result, HttpError> { - let apictx = rqctx.context(); - let nexus = &apictx.nexus; - let path = path_params.into_inner(); - let id = &path.id; - let handler = async { - let opctx = OpContext::for_external_api(&rqctx).await?; - let image = nexus.project_image_fetch_by_id(&opctx, id).await?; - Ok(HttpResponseOk(image.into())) - }; - apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await -} - -/// Fetch a global image by id -#[endpoint { - method = GET, - path = "/by_id/global-images/{id}", - tags = ["images:global"], -}] -async fn global_image_get_by_id( - rqctx: Arc>>, - path_params: Path, -) -> Result, HttpError> { - let apictx = rqctx.context(); - let nexus = &apictx.nexus; - let path = path_params.into_inner(); - let id = &path.id; - let handler = async { - let opctx = OpContext::for_external_api(&rqctx).await?; - let image = nexus.global_image_fetch_by_id(&opctx, id).await?; - Ok(HttpResponseOk(image.into())) - }; - apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await -} - -/// Fetch a snapshot by id -#[endpoint { - method = GET, - path = "/by_id/snapshots/{id}", - tags = ["snapshots"], -}] -async fn snapshot_get_by_id( - rqctx: Arc>>, - path_params: Path, -) -> Result, HttpError> { - let apictx = rqctx.context(); - let nexus = &apictx.nexus; - let path = path_params.into_inner(); - let id = &path.id; - let handler = async { - let opctx = OpContext::for_external_api(&rqctx).await?; - let snapshot = nexus.snapshot_fetch_by_id(&opctx, id).await?; - Ok(HttpResponseOk(snapshot.into())) - }; - apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await -} - -/// Fetch a network interface by id -#[endpoint { - method = GET, - path = "/by_id/network-interfaces/{id}", - tags = ["instances"], -}] -async fn network_interface_get_by_id( - rqctx: Arc>>, - path_params: Path, -) -> Result, HttpError> { - let apictx = rqctx.context(); - let nexus = &apictx.nexus; - let path = path_params.into_inner(); - let id = &path.id; - let handler = async { - let opctx = OpContext::for_external_api(&rqctx).await?; - let network_interface = - nexus.network_interface_fetch_by_id(&opctx, id).await?; - Ok(HttpResponseOk(network_interface.into())) - }; - apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await -} - -/// Fetch a vpc by id -#[endpoint { - method = GET, - path = "/by_id/vpcs/{id}", - tags = ["vpcs"], -}] -async fn vpc_get_by_id( - rqctx: Arc>>, - path_params: Path, -) -> Result, HttpError> { - let apictx = rqctx.context(); - let nexus = &apictx.nexus; - let path = path_params.into_inner(); - let id = &path.id; - let handler = async { - let opctx = OpContext::for_external_api(&rqctx).await?; - let vpc = nexus.vpc_fetch_by_id(&opctx, id).await?; - Ok(HttpResponseOk(vpc.into())) - }; - apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await -} - -/// Fetch a vpc subnet by id -#[endpoint { - method = GET, - path = "/by_id/subnets/{id}", - tags = ["subnets"], -}] -async fn subnet_get_by_id( - rqctx: Arc>>, - path_params: Path, -) -> Result, HttpError> { - let apictx = rqctx.context(); - let nexus = &apictx.nexus; - let path = path_params.into_inner(); - let id = &path.id; - let handler = async { - let opctx = OpContext::for_external_api(&rqctx).await?; - let subnet = nexus.vpc_subnet_fetch_by_id(&opctx, id).await?; - Ok(HttpResponseOk(subnet.into())) - }; - apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await -} - -/// Fetch a vpc router by id -#[endpoint { - method = GET, - path = "/by_id/router/{id}", - tags = ["routers"], -}] -async fn router_get_by_id( - rqctx: Arc>>, - path_params: Path, -) -> Result, HttpError> { - let apictx = rqctx.context(); - let nexus = &apictx.nexus; - let path = path_params.into_inner(); - let id = &path.id; - let handler = async { - let opctx = OpContext::for_external_api(&rqctx).await?; - let router = nexus.vpc_router_fetch_by_id(&opctx, id).await?; - Ok(HttpResponseOk(router.into())) - }; - apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await -} - -/// Fetch a vpc router route by id -#[endpoint { - method = GET, - path = "/by_id/route/{id}", - tags = ["routes"] -}] -async fn route_get_by_id( - rqctx: Arc>>, - path_params: Path, -) -> Result, HttpError> { - let apictx = rqctx.context(); - let nexus = &apictx.nexus; - let path = path_params.into_inner(); - let id = &path.id; - let handler = async { - let opctx = OpContext::for_external_api(&rqctx).await?; - let route = nexus.route_fetch_by_id(&opctx, id).await?; - Ok(HttpResponseOk(route.into())) - }; - apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await -} From f0c4b80e5ddaccfb7b00081d270333e7bb819cbc Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Wed, 6 Jul 2022 20:09:30 -0400 Subject: [PATCH 10/18] Reorganize implementations, fix some tags --- nexus/src/external_api/http_entrypoints.rs | 6 +-- nexus/tests/output/nexus_tags.txt | 13 +++++- openapi/nexus.json | 54 +++++++++++----------- 3 files changed, 42 insertions(+), 31 deletions(-) diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index 668ed78f1f..347cd6125b 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -2919,7 +2919,7 @@ async fn vpc_subnet_view( #[endpoint { method = GET, path = "/by_id/subnets/{id}", - tags = ["subnets"], + tags = ["vpcs"], }] async fn vpc_subnet_view_by_id( rqctx: Arc>>, @@ -3220,7 +3220,7 @@ async fn vpc_router_view( #[endpoint { method = GET, path = "/by_id/router/{id}", - tags = ["routers"], + tags = ["vpcs"], }] async fn vpc_router_view_by_id( rqctx: Arc>>, @@ -3415,7 +3415,7 @@ async fn vpc_router_route_view( #[endpoint { method = GET, path = "/by_id/route/{id}", - tags = ["routes"] + tags = ["vpcs"] }] async fn vpc_router_route_view_by_id( rqctx: Arc>>, diff --git a/nexus/tests/output/nexus_tags.txt b/nexus/tests/output/nexus_tags.txt index c754177a42..a28c26e330 100644 --- a/nexus/tests/output/nexus_tags.txt +++ b/nexus/tests/output/nexus_tags.txt @@ -1,10 +1,10 @@ API operations found with tag "disks" OPERATION ID URL PATH -disk_get_by_id /by_id/disks/{id} disk_create /organizations/{organization_name}/projects/{project_name}/disks disk_delete /organizations/{organization_name}/projects/{project_name}/disks/{disk_name} disk_list /organizations/{organization_name}/projects/{project_name}/disks disk_view /organizations/{organization_name}/projects/{project_name}/disks/{disk_name} +disk_view_by_id /by_id/disks/{id} API operations found with tag "hardware" OPERATION ID URL PATH @@ -29,6 +29,7 @@ image_create /organizations/{organization_name}/proj image_delete /organizations/{organization_name}/projects/{project_name}/images/{image_name} image_list /organizations/{organization_name}/projects/{project_name}/images image_view /organizations/{organization_name}/projects/{project_name}/images/{image_name} +image_view_by_id /by_id/images/{id} API operations found with tag "images:global" OPERATION ID URL PATH @@ -36,6 +37,7 @@ image_global_create /images image_global_delete /images/{image_name} image_global_list /images image_global_view /images/{image_name} +image_global_view_by_id /by_id/global-images/{id} API operations found with tag "instances" OPERATION ID URL PATH @@ -51,11 +53,13 @@ instance_network_interface_delete /organizations/{organization_name}/proj instance_network_interface_list /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/network-interfaces instance_network_interface_update /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/network-interfaces/{interface_name} instance_network_interface_view /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/network-interfaces/{interface_name} +instance_network_interface_view_by_id /by_id/network-interfaces/{id} instance_reboot /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/reboot instance_serial_console /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/serial-console instance_start /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/start instance_stop /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/stop instance_view /organizations/{organization_name}/projects/{project_name}/instances/{instance_name} +instance_view_by_id /by_id/instances/{id} API operations found with tag "ip-pools" OPERATION ID URL PATH @@ -86,6 +90,7 @@ organization_policy_update /organizations/{organization_name}/poli organization_policy_view /organizations/{organization_name}/policy organization_update /organizations/{organization_name} organization_view /organizations/{organization_name} +organization_view_by_id /by_id/organizations/{id} API operations found with tag "policy" OPERATION ID URL PATH @@ -101,6 +106,7 @@ project_policy_update /organizations/{organization_name}/proj project_policy_view /organizations/{organization_name}/projects/{project_name}/policy project_update /organizations/{organization_name}/projects/{project_name} project_view /organizations/{organization_name}/projects/{project_name} +project_view_by_id /by_id/projects/{id} API operations found with tag "roles" OPERATION ID URL PATH @@ -138,6 +144,7 @@ snapshot_create /organizations/{organization_name}/proj snapshot_delete /organizations/{organization_name}/projects/{project_name}/snapshots/{snapshot_name} snapshot_list /organizations/{organization_name}/projects/{project_name}/snapshots snapshot_view /organizations/{organization_name}/projects/{project_name}/snapshots/{snapshot_name} +snapshot_view_by_id /by_id/snapshots/{id} API operations found with tag "system" OPERATION ID URL PATH @@ -163,14 +170,18 @@ vpc_router_route_delete /organizations/{organization_name}/proj vpc_router_route_list /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name}/routes vpc_router_route_update /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name}/routes/{route_name} vpc_router_route_view /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name}/routes/{route_name} +vpc_router_route_view_by_id /by_id/route/{id} vpc_router_update /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name} vpc_router_view /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name} +vpc_router_view_by_id /by_id/router/{id} vpc_subnet_create /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets vpc_subnet_delete /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets/{subnet_name} vpc_subnet_list /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets vpc_subnet_list_network_interfaces /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets/{subnet_name}/network-interfaces vpc_subnet_update /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets/{subnet_name} vpc_subnet_view /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets/{subnet_name} +vpc_subnet_view_by_id /by_id/subnets/{id} vpc_update /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name} vpc_view /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name} +vpc_view_by_id /by_id/vpcs/{id} diff --git a/openapi/nexus.json b/openapi/nexus.json index 94e828b8a4..28fee10195 100644 --- a/openapi/nexus.json +++ b/openapi/nexus.json @@ -15,8 +15,8 @@ "tags": [ "disks" ], - "summary": "Fetch a disk by id", - "operationId": "disk_get_by_id", + "summary": "Get a disk by id", + "operationId": "disk_view_by_id", "parameters": [ { "in": "path", @@ -54,8 +54,8 @@ "tags": [ "images:global" ], - "summary": "Fetch a global image by id", - "operationId": "global_image_get_by_id", + "summary": "Get a global image by id.", + "operationId": "image_global_view_by_id", "parameters": [ { "in": "path", @@ -94,7 +94,7 @@ "images" ], "summary": "Fetch an image by id", - "operationId": "image_get_by_id", + "operationId": "image_view_by_id", "parameters": [ { "in": "path", @@ -132,8 +132,8 @@ "tags": [ "instances" ], - "summary": "Fetch an instance by id", - "operationId": "instance_get_by_id", + "summary": "Get an instance by id.", + "operationId": "instance_view_by_id", "parameters": [ { "in": "path", @@ -171,8 +171,8 @@ "tags": [ "instances" ], - "summary": "Fetch a network interface by id", - "operationId": "network_interface_get_by_id", + "summary": "Get an instance's network interface by id.", + "operationId": "instance_network_interface_view_by_id", "parameters": [ { "in": "path", @@ -210,8 +210,8 @@ "tags": [ "organizations" ], - "summary": "Fetch an organization by id", - "operationId": "organization_get_by_id", + "summary": "Get an organization by id", + "operationId": "organization_view_by_id", "parameters": [ { "in": "path", @@ -249,8 +249,8 @@ "tags": [ "projects" ], - "summary": "Fetch a project by id", - "operationId": "project_get_by_id", + "summary": "Get a project by id", + "operationId": "project_view_by_id", "parameters": [ { "in": "path", @@ -286,10 +286,10 @@ "/by_id/route/{id}": { "get": { "tags": [ - "routes" + "vpcs" ], - "summary": "Fetch a vpc router route by id", - "operationId": "route_get_by_id", + "summary": "Get a vpc router route by id", + "operationId": "vpc_router_route_view_by_id", "parameters": [ { "in": "path", @@ -325,10 +325,10 @@ "/by_id/router/{id}": { "get": { "tags": [ - "routers" + "vpcs" ], - "summary": "Fetch a vpc router by id", - "operationId": "router_get_by_id", + "summary": "Get a VPC Router by id", + "operationId": "vpc_router_view_by_id", "parameters": [ { "in": "path", @@ -366,8 +366,8 @@ "tags": [ "snapshots" ], - "summary": "Fetch a snapshot by id", - "operationId": "snapshot_get_by_id", + "summary": "Get a snapshot by id.", + "operationId": "snapshot_view_by_id", "parameters": [ { "in": "path", @@ -403,10 +403,10 @@ "/by_id/subnets/{id}": { "get": { "tags": [ - "subnets" + "vpcs" ], - "summary": "Fetch a vpc subnet by id", - "operationId": "subnet_get_by_id", + "summary": "Get a VPC subnet by id.", + "operationId": "vpc_subnet_view_by_id", "parameters": [ { "in": "path", @@ -444,8 +444,8 @@ "tags": [ "vpcs" ], - "summary": "Fetch a vpc by id", - "operationId": "vpc_get_by_id", + "summary": "Get a VPC by id.", + "operationId": "vpc_view_by_id", "parameters": [ { "in": "path", @@ -2173,7 +2173,7 @@ "tags": [ "disks" ], - "summary": "Fetch a single disk in a project.", + "summary": "Get a single disk in a project.", "operationId": "disk_view", "parameters": [ { From 5b0e6b342dc5a29b2cac96531bd10dc2f0296f24 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Wed, 6 Jul 2022 21:23:42 -0400 Subject: [PATCH 11/18] by_id to by-id --- nexus/src/external_api/http_entrypoints.rs | 31 +++++++++++++--------- nexus/tests/output/nexus_tags.txt | 24 ++++++++--------- openapi/nexus.json | 24 ++++++++--------- 3 files changed, 42 insertions(+), 37 deletions(-) diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index 9e720bf684..c6a52d3fb0 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -265,6 +265,11 @@ pub fn external_api() -> NexusApiDescription { // DELETE /organizations/{org_name} (delete a organization in the collection) // PUT /organizations/{org_name} (update a organization in the collection) // +// An exception to this are id lookup operations which have a different top-level route +// but will still be grouped with the collection. For example: +// +// GET /by-id/organizations/{org_name} (look up a organization in the collection by id) +// // We pick a name for the function that implements a given API entrypoint // based on how we expect it to appear in the CLI subcommand hierarchy. For // example: @@ -312,7 +317,7 @@ async fn policy_view( } -/// Path parameters for `/by_id/` endpoints +/// Path parameters for `/by-id/` endpoints #[derive(Deserialize, JsonSchema)] struct ByIdPathParams { id: Uuid, @@ -716,7 +721,7 @@ async fn organization_view( /// Get an organization by id #[endpoint { method = GET, - path = "/by_id/organizations/{id}", + path = "/by-id/organizations/{id}", tags = ["organizations"], }] async fn organization_view_by_id( @@ -971,7 +976,7 @@ async fn project_view( /// Get a project by id #[endpoint { method = GET, - path = "/by_id/projects/{id}", + path = "/by-id/projects/{id}", tags = ["projects"], }] async fn project_view_by_id( @@ -1456,7 +1461,7 @@ async fn disk_view( /// Get a disk by id #[endpoint { method = GET, - path = "/by_id/disks/{id}", + path = "/by-id/disks/{id}", tags = ["disks"], }] async fn disk_view_by_id( @@ -1629,7 +1634,7 @@ async fn instance_view( /// Get an instance by id. #[endpoint { method = GET, - path = "/by_id/instances/{id}", + path = "/by-id/instances/{id}", tags = ["instances"], }] async fn instance_view_by_id( @@ -2043,7 +2048,7 @@ async fn image_global_view( /// Get a global image by id. #[endpoint { method = GET, - path = "/by_id/global-images/{id}", + path = "/by-id/global-images/{id}", tags = ["images:global"], }] async fn image_global_view_by_id( @@ -2209,7 +2214,7 @@ async fn image_view( /// Fetch an image by id #[endpoint { method = GET, - path = "/by_id/images/{id}", + path = "/by-id/images/{id}", tags = ["images"], }] async fn image_view_by_id( @@ -2425,7 +2430,7 @@ async fn instance_network_interface_view( /// Get an instance's network interface by id. #[endpoint { method = GET, - path = "/by_id/network-interfaces/{id}", + path = "/by-id/network-interfaces/{id}", tags = ["instances"], }] async fn instance_network_interface_view_by_id( @@ -2597,7 +2602,7 @@ async fn snapshot_view( /// Get a snapshot by id. #[endpoint { method = GET, - path = "/by_id/snapshots/{id}", + path = "/by-id/snapshots/{id}", tags = ["snapshots"], }] async fn snapshot_view_by_id( @@ -2727,7 +2732,7 @@ async fn vpc_view( /// Get a VPC by id. #[endpoint { method = GET, - path = "/by_id/vpcs/{id}", + path = "/by-id/vpcs/{id}", tags = ["vpcs"], }] async fn vpc_view_by_id( @@ -2919,7 +2924,7 @@ async fn vpc_subnet_view( /// Get a VPC subnet by id. #[endpoint { method = GET, - path = "/by_id/subnets/{id}", + path = "/by-id/subnets/{id}", tags = ["vpcs"], }] async fn vpc_subnet_view_by_id( @@ -3220,7 +3225,7 @@ async fn vpc_router_view( /// Get a VPC Router by id #[endpoint { method = GET, - path = "/by_id/router/{id}", + path = "/by-id/router/{id}", tags = ["vpcs"], }] async fn vpc_router_view_by_id( @@ -3415,7 +3420,7 @@ async fn vpc_router_route_view( /// Get a vpc router route by id #[endpoint { method = GET, - path = "/by_id/route/{id}", + path = "/by-id/route/{id}", tags = ["vpcs"] }] async fn vpc_router_route_view_by_id( diff --git a/nexus/tests/output/nexus_tags.txt b/nexus/tests/output/nexus_tags.txt index a28c26e330..b0b0d80874 100644 --- a/nexus/tests/output/nexus_tags.txt +++ b/nexus/tests/output/nexus_tags.txt @@ -4,7 +4,7 @@ disk_create /organizations/{organization_name}/proj disk_delete /organizations/{organization_name}/projects/{project_name}/disks/{disk_name} disk_list /organizations/{organization_name}/projects/{project_name}/disks disk_view /organizations/{organization_name}/projects/{project_name}/disks/{disk_name} -disk_view_by_id /by_id/disks/{id} +disk_view_by_id /by-id/disks/{id} API operations found with tag "hardware" OPERATION ID URL PATH @@ -29,7 +29,7 @@ image_create /organizations/{organization_name}/proj image_delete /organizations/{organization_name}/projects/{project_name}/images/{image_name} image_list /organizations/{organization_name}/projects/{project_name}/images image_view /organizations/{organization_name}/projects/{project_name}/images/{image_name} -image_view_by_id /by_id/images/{id} +image_view_by_id /by-id/images/{id} API operations found with tag "images:global" OPERATION ID URL PATH @@ -37,7 +37,7 @@ image_global_create /images image_global_delete /images/{image_name} image_global_list /images image_global_view /images/{image_name} -image_global_view_by_id /by_id/global-images/{id} +image_global_view_by_id /by-id/global-images/{id} API operations found with tag "instances" OPERATION ID URL PATH @@ -53,13 +53,13 @@ instance_network_interface_delete /organizations/{organization_name}/proj instance_network_interface_list /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/network-interfaces instance_network_interface_update /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/network-interfaces/{interface_name} instance_network_interface_view /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/network-interfaces/{interface_name} -instance_network_interface_view_by_id /by_id/network-interfaces/{id} +instance_network_interface_view_by_id /by-id/network-interfaces/{id} instance_reboot /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/reboot instance_serial_console /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/serial-console instance_start /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/start instance_stop /organizations/{organization_name}/projects/{project_name}/instances/{instance_name}/stop instance_view /organizations/{organization_name}/projects/{project_name}/instances/{instance_name} -instance_view_by_id /by_id/instances/{id} +instance_view_by_id /by-id/instances/{id} API operations found with tag "ip-pools" OPERATION ID URL PATH @@ -90,7 +90,7 @@ organization_policy_update /organizations/{organization_name}/poli organization_policy_view /organizations/{organization_name}/policy organization_update /organizations/{organization_name} organization_view /organizations/{organization_name} -organization_view_by_id /by_id/organizations/{id} +organization_view_by_id /by-id/organizations/{id} API operations found with tag "policy" OPERATION ID URL PATH @@ -106,7 +106,7 @@ project_policy_update /organizations/{organization_name}/proj project_policy_view /organizations/{organization_name}/projects/{project_name}/policy project_update /organizations/{organization_name}/projects/{project_name} project_view /organizations/{organization_name}/projects/{project_name} -project_view_by_id /by_id/projects/{id} +project_view_by_id /by-id/projects/{id} API operations found with tag "roles" OPERATION ID URL PATH @@ -144,7 +144,7 @@ snapshot_create /organizations/{organization_name}/proj snapshot_delete /organizations/{organization_name}/projects/{project_name}/snapshots/{snapshot_name} snapshot_list /organizations/{organization_name}/projects/{project_name}/snapshots snapshot_view /organizations/{organization_name}/projects/{project_name}/snapshots/{snapshot_name} -snapshot_view_by_id /by_id/snapshots/{id} +snapshot_view_by_id /by-id/snapshots/{id} API operations found with tag "system" OPERATION ID URL PATH @@ -170,18 +170,18 @@ vpc_router_route_delete /organizations/{organization_name}/proj vpc_router_route_list /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name}/routes vpc_router_route_update /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name}/routes/{route_name} vpc_router_route_view /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name}/routes/{route_name} -vpc_router_route_view_by_id /by_id/route/{id} +vpc_router_route_view_by_id /by-id/route/{id} vpc_router_update /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name} vpc_router_view /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name} -vpc_router_view_by_id /by_id/router/{id} +vpc_router_view_by_id /by-id/router/{id} vpc_subnet_create /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets vpc_subnet_delete /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets/{subnet_name} vpc_subnet_list /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets vpc_subnet_list_network_interfaces /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets/{subnet_name}/network-interfaces vpc_subnet_update /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets/{subnet_name} vpc_subnet_view /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets/{subnet_name} -vpc_subnet_view_by_id /by_id/subnets/{id} +vpc_subnet_view_by_id /by-id/subnets/{id} vpc_update /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name} vpc_view /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name} -vpc_view_by_id /by_id/vpcs/{id} +vpc_view_by_id /by-id/vpcs/{id} diff --git a/openapi/nexus.json b/openapi/nexus.json index 28fee10195..48462c1a7f 100644 --- a/openapi/nexus.json +++ b/openapi/nexus.json @@ -10,7 +10,7 @@ "version": "0.0.1" }, "paths": { - "/by_id/disks/{id}": { + "/by-id/disks/{id}": { "get": { "tags": [ "disks" @@ -49,7 +49,7 @@ } } }, - "/by_id/global-images/{id}": { + "/by-id/global-images/{id}": { "get": { "tags": [ "images:global" @@ -88,7 +88,7 @@ } } }, - "/by_id/images/{id}": { + "/by-id/images/{id}": { "get": { "tags": [ "images" @@ -127,7 +127,7 @@ } } }, - "/by_id/instances/{id}": { + "/by-id/instances/{id}": { "get": { "tags": [ "instances" @@ -166,7 +166,7 @@ } } }, - "/by_id/network-interfaces/{id}": { + "/by-id/network-interfaces/{id}": { "get": { "tags": [ "instances" @@ -205,7 +205,7 @@ } } }, - "/by_id/organizations/{id}": { + "/by-id/organizations/{id}": { "get": { "tags": [ "organizations" @@ -244,7 +244,7 @@ } } }, - "/by_id/projects/{id}": { + "/by-id/projects/{id}": { "get": { "tags": [ "projects" @@ -283,7 +283,7 @@ } } }, - "/by_id/route/{id}": { + "/by-id/route/{id}": { "get": { "tags": [ "vpcs" @@ -322,7 +322,7 @@ } } }, - "/by_id/router/{id}": { + "/by-id/router/{id}": { "get": { "tags": [ "vpcs" @@ -361,7 +361,7 @@ } } }, - "/by_id/snapshots/{id}": { + "/by-id/snapshots/{id}": { "get": { "tags": [ "snapshots" @@ -400,7 +400,7 @@ } } }, - "/by_id/subnets/{id}": { + "/by-id/subnets/{id}": { "get": { "tags": [ "vpcs" @@ -439,7 +439,7 @@ } } }, - "/by_id/vpcs/{id}": { + "/by-id/vpcs/{id}": { "get": { "tags": [ "vpcs" From 508cdd4b12ee566131c4f39c64adefcdf241f691 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Wed, 6 Jul 2022 22:05:21 -0400 Subject: [PATCH 12/18] Prefix vpc resources --- nexus/src/external_api/http_entrypoints.rs | 6 ++--- nexus/tests/output/nexus_tags.txt | 6 ++--- openapi/nexus.json | 30 +++++++++++----------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index c6a52d3fb0..3fe715bba1 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -2924,7 +2924,7 @@ async fn vpc_subnet_view( /// Get a VPC subnet by id. #[endpoint { method = GET, - path = "/by-id/subnets/{id}", + path = "/by-id/vpc-subnets/{id}", tags = ["vpcs"], }] async fn vpc_subnet_view_by_id( @@ -3225,7 +3225,7 @@ async fn vpc_router_view( /// Get a VPC Router by id #[endpoint { method = GET, - path = "/by-id/router/{id}", + path = "/by-id/vpc-routers/{id}", tags = ["vpcs"], }] async fn vpc_router_view_by_id( @@ -3420,7 +3420,7 @@ async fn vpc_router_route_view( /// Get a vpc router route by id #[endpoint { method = GET, - path = "/by-id/route/{id}", + path = "/by-id/vpc-router-routes/{id}", tags = ["vpcs"] }] async fn vpc_router_route_view_by_id( diff --git a/nexus/tests/output/nexus_tags.txt b/nexus/tests/output/nexus_tags.txt index b0b0d80874..5b483a32c1 100644 --- a/nexus/tests/output/nexus_tags.txt +++ b/nexus/tests/output/nexus_tags.txt @@ -170,17 +170,17 @@ vpc_router_route_delete /organizations/{organization_name}/proj vpc_router_route_list /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name}/routes vpc_router_route_update /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name}/routes/{route_name} vpc_router_route_view /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name}/routes/{route_name} -vpc_router_route_view_by_id /by-id/route/{id} +vpc_router_route_view_by_id /by-id/vpc-router-routes/{id} vpc_router_update /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name} vpc_router_view /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/routers/{router_name} -vpc_router_view_by_id /by-id/router/{id} +vpc_router_view_by_id /by-id/vpc-routers/{id} vpc_subnet_create /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets vpc_subnet_delete /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets/{subnet_name} vpc_subnet_list /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets vpc_subnet_list_network_interfaces /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets/{subnet_name}/network-interfaces vpc_subnet_update /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets/{subnet_name} vpc_subnet_view /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name}/subnets/{subnet_name} -vpc_subnet_view_by_id /by-id/subnets/{id} +vpc_subnet_view_by_id /by-id/vpc-subnets/{id} vpc_update /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name} vpc_view /organizations/{organization_name}/projects/{project_name}/vpcs/{vpc_name} vpc_view_by_id /by-id/vpcs/{id} diff --git a/openapi/nexus.json b/openapi/nexus.json index 48462c1a7f..0f89f5192f 100644 --- a/openapi/nexus.json +++ b/openapi/nexus.json @@ -283,13 +283,13 @@ } } }, - "/by-id/route/{id}": { + "/by-id/snapshots/{id}": { "get": { "tags": [ - "vpcs" + "snapshots" ], - "summary": "Get a vpc router route by id", - "operationId": "vpc_router_route_view_by_id", + "summary": "Get a snapshot by id.", + "operationId": "snapshot_view_by_id", "parameters": [ { "in": "path", @@ -308,7 +308,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RouterRoute" + "$ref": "#/components/schemas/Snapshot" } } } @@ -322,13 +322,13 @@ } } }, - "/by-id/router/{id}": { + "/by-id/vpc-router-routes/{id}": { "get": { "tags": [ "vpcs" ], - "summary": "Get a VPC Router by id", - "operationId": "vpc_router_view_by_id", + "summary": "Get a vpc router route by id", + "operationId": "vpc_router_route_view_by_id", "parameters": [ { "in": "path", @@ -347,7 +347,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VpcRouter" + "$ref": "#/components/schemas/RouterRoute" } } } @@ -361,13 +361,13 @@ } } }, - "/by-id/snapshots/{id}": { + "/by-id/vpc-routers/{id}": { "get": { "tags": [ - "snapshots" + "vpcs" ], - "summary": "Get a snapshot by id.", - "operationId": "snapshot_view_by_id", + "summary": "Get a VPC Router by id", + "operationId": "vpc_router_view_by_id", "parameters": [ { "in": "path", @@ -386,7 +386,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Snapshot" + "$ref": "#/components/schemas/VpcRouter" } } } @@ -400,7 +400,7 @@ } } }, - "/by-id/subnets/{id}": { + "/by-id/vpc-subnets/{id}": { "get": { "tags": [ "vpcs" From 77d3a503f21abca3a80676b1b975352afb0f70fc Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Thu, 7 Jul 2022 14:25:57 -0400 Subject: [PATCH 13/18] Add mechanism to valid auth endpoints with ids --- nexus/src/external_api/http_entrypoints.rs | 2 - nexus/test-utils/src/http_testing.rs | 2 +- nexus/tests/integration_tests/endpoints.rs | 30 ++++++++ nexus/tests/integration_tests/unauthorized.rs | 70 +++++++++++++++---- 4 files changed, 87 insertions(+), 17 deletions(-) diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index 3fe715bba1..b6464ac657 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -316,7 +316,6 @@ async fn policy_view( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } - /// Path parameters for `/by-id/` endpoints #[derive(Deserialize, JsonSchema)] struct ByIdPathParams { @@ -2233,7 +2232,6 @@ async fn image_view_by_id( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } - /// Delete an image /// /// Permanently delete an image from a project. This operation cannot be undone. diff --git a/nexus/test-utils/src/http_testing.rs b/nexus/test-utils/src/http_testing.rs index 03122218bc..ac00ddbc29 100644 --- a/nexus/test-utils/src/http_testing.rs +++ b/nexus/test-utils/src/http_testing.rs @@ -427,7 +427,7 @@ where } /// Represents a response from an HTTP server -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TestResponse { pub status: http::StatusCode, pub headers: http::HeaderMap, diff --git a/nexus/tests/integration_tests/endpoints.rs b/nexus/tests/integration_tests/endpoints.rs index cbb1dfd6ca..2b56b76dd6 100644 --- a/nexus/tests/integration_tests/endpoints.rs +++ b/nexus/tests/integration_tests/endpoints.rs @@ -595,6 +595,13 @@ lazy_static! { ), ], }, + VerifyEndpoint { + url: "/by-id/organizations/{id}", + visibility: Visibility::Public, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, VerifyEndpoint { url: &*DEMO_ORG_POLICY_URL, visibility: Visibility::Protected, @@ -630,6 +637,13 @@ lazy_static! { ), ], }, + VerifyEndpoint { + url: "/by-id/projects/{id}", + visibility: Visibility::Public, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, VerifyEndpoint { url: &*DEMO_PROJECT_URL, visibility: Visibility::Protected, @@ -673,6 +687,14 @@ lazy_static! { ], }, + VerifyEndpoint { + url: "/by-id/vpcs/{id}", + visibility: Visibility::Public, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + VerifyEndpoint { url: &*DEMO_VPC_URL, visibility: Visibility::Protected, @@ -717,6 +739,14 @@ lazy_static! { ], }, + VerifyEndpoint { + url: "/by-id/vpc-subnets/{id}", + visibility: Visibility::Public, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + VerifyEndpoint { url: &*DEMO_VPC_SUBNET_URL, visibility: Visibility::Protected, diff --git a/nexus/tests/integration_tests/unauthorized.rs b/nexus/tests/integration_tests/unauthorized.rs index d83f63c64c..497856bacc 100644 --- a/nexus/tests/integration_tests/unauthorized.rs +++ b/nexus/tests/integration_tests/unauthorized.rs @@ -7,6 +7,7 @@ use super::endpoints::*; use crate::integration_tests::saml::SAML_IDP_DESCRIPTOR; +use diesel::expression::AsExpression; use dropshot::test_util::ClientTestContext; use dropshot::HttpErrorResponseBody; use headers::authorization::Credentials; @@ -21,6 +22,7 @@ use nexus_test_utils::http_testing::TestResponse; use nexus_test_utils::resource_helpers::DiskTest; use nexus_test_utils::ControlPlaneTestContext; use nexus_test_utils_macros::nexus_test; +use omicron_common::api::external::IdentityMetadata; use omicron_nexus::authn::external::spoof; // This test hits a list Nexus API endpoints using both unauthenticated and @@ -55,22 +57,29 @@ async fn test_unauthorized(cptestctx: &ControlPlaneTestContext) { DiskTest::new(cptestctx).await; let client = &cptestctx.external_client; let log = &cptestctx.logctx.log; + let mut setup_results = std::collections::BTreeMap::new(); // Create test data. info!(log, "setting up resource hierarchy"); for request in &*SETUP_REQUESTS { - NexusRequest::objects_post(client, request.url, &request.body) - .authn_as(AuthnMode::PrivilegedUser) - .execute() - .await - .unwrap(); + let result = + NexusRequest::objects_post(client, request.url, &request.body) + .authn_as(AuthnMode::PrivilegedUser) + .execute() + .await + .unwrap(); + + setup_results.insert(request.url, result.clone()); + request.id_routes.iter().for_each(|id_route| { + setup_results.insert(id_route, result.clone()); + }); } // Verify the hardcoded endpoints. info!(log, "verifying endpoints"); print!("{}", VERIFY_HEADER); for endpoint in &*VERIFY_ENDPOINTS { - verify_endpoint(&log, client, endpoint).await; + verify_endpoint(&log, client, endpoint, &setup_results).await; } } @@ -121,6 +130,8 @@ struct SetupReq { url: &'static str, /// body of the `POST` request body: serde_json::Value, + /// populates `id` params for later route lookups + id_routes: Vec<&'static str>, } lazy_static! { @@ -155,66 +166,79 @@ lazy_static! { SetupReq { url: "/silos", body: serde_json::to_value(&*DEMO_SILO_CREATE).unwrap(), + id_routes: vec!["/by-id/silos/{id}"], }, // Create an IP pool SetupReq { url: &*DEMO_IP_POOLS_URL, body: serde_json::to_value(&*DEMO_IP_POOL_CREATE).unwrap(), + id_routes: vec!["/by-id/ip-pools/{id}"], }, // Create an IP Pool range SetupReq { url: &*DEMO_IP_POOL_RANGES_ADD_URL, body: serde_json::to_value(&*DEMO_IP_POOL_RANGE).unwrap(), + id_routes: vec![], }, // Create an Organization SetupReq { url: "/organizations", - body: serde_json::to_value(&*DEMO_ORG_CREATE).unwrap() + body: serde_json::to_value(&*DEMO_ORG_CREATE).unwrap(), + id_routes: vec!["/by-id/organizations/{id}"], }, // Create a Project in the Organization SetupReq { url: &*DEMO_ORG_PROJECTS_URL, body: serde_json::to_value(&*DEMO_PROJECT_CREATE).unwrap(), + id_routes: vec!["/by-id/projects/{id}"], }, // Create a VPC in the Project SetupReq { url: &*DEMO_PROJECT_URL_VPCS, body: serde_json::to_value(&*DEMO_VPC_CREATE).unwrap(), + id_routes: vec!["/by-id/vpcs/{id}"], }, // Create a VPC Subnet in the Vpc SetupReq { url: &*DEMO_VPC_URL_SUBNETS, body: serde_json::to_value(&*DEMO_VPC_SUBNET_CREATE).unwrap(), + id_routes: vec!["/by-id/vpc-subnets/{id}"], }, // Create a VPC Router in the Vpc SetupReq { url: &*DEMO_VPC_URL_ROUTERS, body: serde_json::to_value(&*DEMO_VPC_ROUTER_CREATE).unwrap(), + id_routes: vec!["/by-id/vpc-routers/{id}"], }, // Create a VPC Router in the Vpc SetupReq { url: &*DEMO_VPC_ROUTER_URL_ROUTES, body: serde_json::to_value(&*DEMO_ROUTER_ROUTE_CREATE).unwrap(), + id_routes: vec!["/by-id/vpc-router-routes/{id}"], }, // Create a Disk in the Project SetupReq { url: &*DEMO_PROJECT_URL_DISKS, body: serde_json::to_value(&*DEMO_DISK_CREATE).unwrap(), + id_routes: vec!["/by-id/disks/{id}"], }, // Create an Instance in the Project SetupReq { url: &*DEMO_PROJECT_URL_INSTANCES, body: serde_json::to_value(&*DEMO_INSTANCE_CREATE).unwrap(), + id_routes: vec!["/by-id/instances/{id}"], }, // Create a GlobalImage SetupReq { url: "/images", body: serde_json::to_value(&*DEMO_GLOBAL_IMAGE_CREATE).unwrap(), + id_routes: vec!["/by-id/global-images/{id}"], }, // Create a SAML identity provider SetupReq { url: &*SAML_IDENTITY_PROVIDERS_URL, body: serde_json::to_value(&*SAML_IDENTITY_PROVIDER).unwrap(), + id_routes: vec![], }, ]; } @@ -273,6 +297,7 @@ async fn verify_endpoint( log: &slog::Logger, client: &ClientTestContext, endpoint: &VerifyEndpoint, + setup_results: &std::collections::BTreeMap<&str, TestResponse>, ) { let log = log.new(o!("url" => endpoint.url)); info!(log, "test: begin endpoint"); @@ -288,6 +313,23 @@ async fn verify_endpoint( Visibility::Protected => StatusCode::NOT_FOUND, }; + // For routes with an id param, find their matching id in the setup results. + let uri = if endpoint.url.contains("{id}") { + endpoint.url.replace( + "{id}", + &setup_results + .get(endpoint.url) + .unwrap() + .parsed_body::() + .unwrap() + .id + .to_string() + .as_str(), + ) + } else { + endpoint.url.to_string() + }; + // Make one GET request as an authorized user to make sure we get a "200 OK" // response. Otherwise, the test might later succeed by coincidence. We // might find a 404 because of something that actually doesn't exist rather @@ -302,7 +344,7 @@ async fn verify_endpoint( &http::StatusCode::OK, ))); Some( - NexusRequest::object_get(client, endpoint.url) + NexusRequest::object_get(client, uri.as_str()) .authn_as(AuthnMode::PrivilegedUser) .execute() .await @@ -319,7 +361,7 @@ async fn verify_endpoint( client, expected_status, http::Method::GET, - endpoint.url, + uri.as_str(), ) .authn_as(AuthnMode::PrivilegedUser) .execute() @@ -356,7 +398,7 @@ async fn verify_endpoint( None => StatusCode::METHOD_NOT_ALLOWED, }; let response = NexusRequest::new( - RequestBuilder::new(client, method.clone(), endpoint.url) + RequestBuilder::new(client, method.clone(), uri.as_str()) .body(body.as_ref()) .expect_status(Some(expected_status)), ) @@ -374,7 +416,7 @@ async fn verify_endpoint( None => StatusCode::METHOD_NOT_ALLOWED, }; let response = - RequestBuilder::new(client, method.clone(), endpoint.url) + RequestBuilder::new(client, method.clone(), uri.as_str()) .body(body.as_ref()) .expect_status(Some(expected_status)) .execute() @@ -402,7 +444,7 @@ async fn verify_endpoint( info!(log, "test: bogus creds: bad actor"; "method" => ?method); let bad_actor_authn_header = &spoof::SPOOF_HEADER_BAD_ACTOR; let response = - RequestBuilder::new(client, method.clone(), endpoint.url) + RequestBuilder::new(client, method.clone(), uri.as_str()) .body(body.as_ref()) .expect_status(Some(expected_status)) .header( @@ -419,7 +461,7 @@ async fn verify_endpoint( info!(log, "test: bogus creds: bad cred syntax"; "method" => ?method); let bad_creds_authn_header = &spoof::SPOOF_HEADER_BAD_CREDS; let response = - RequestBuilder::new(client, method.clone(), endpoint.url) + RequestBuilder::new(client, method.clone(), uri.as_str()) .body(body.as_ref()) .expect_status(Some(expected_status)) .header( @@ -450,7 +492,7 @@ async fn verify_endpoint( info!(log, "test: compare current resource content with earlier"); if let Some(resource_before) = resource_before { let resource_after: serde_json::Value = - NexusRequest::object_get(client, endpoint.url) + NexusRequest::object_get(client, uri.as_str()) .authn_as(AuthnMode::PrivilegedUser) .execute() .await From cb95f77c8fd09e535f7b510b6862af26e9a3a63d Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Thu, 7 Jul 2022 15:33:29 -0400 Subject: [PATCH 14/18] Add coverage for by-id unauthed tests --- nexus/tests/integration_tests/endpoints.rs | 86 +++++++++++++++++-- nexus/tests/integration_tests/unauthorized.rs | 1 - 2 files changed, 79 insertions(+), 8 deletions(-) diff --git a/nexus/tests/integration_tests/endpoints.rs b/nexus/tests/integration_tests/endpoints.rs index 2b56b76dd6..50a3bf98e7 100644 --- a/nexus/tests/integration_tests/endpoints.rs +++ b/nexus/tests/integration_tests/endpoints.rs @@ -579,6 +579,15 @@ lazy_static! { ) ], }, + + VerifyEndpoint { + url: "/by-id/organizations/{id}", + visibility: Visibility::Public, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + VerifyEndpoint { url: &*DEMO_ORG_URL, visibility: Visibility::Protected, @@ -595,13 +604,7 @@ lazy_static! { ), ], }, - VerifyEndpoint { - url: "/by-id/organizations/{id}", - visibility: Visibility::Public, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, + VerifyEndpoint { url: &*DEMO_ORG_POLICY_URL, visibility: Visibility::Protected, @@ -637,6 +640,7 @@ lazy_static! { ), ], }, + VerifyEndpoint { url: "/by-id/projects/{id}", visibility: Visibility::Public, @@ -644,6 +648,7 @@ lazy_static! { AllowedMethod::Get, ], }, + VerifyEndpoint { url: &*DEMO_PROJECT_URL, visibility: Visibility::Protected, @@ -660,6 +665,7 @@ lazy_static! { ), ], }, + VerifyEndpoint { url: &*DEMO_PROJECT_POLICY_URL, visibility: Visibility::Protected, @@ -785,6 +791,14 @@ lazy_static! { ], }, + VerifyEndpoint { + url: "/by-id/vpc-routers/{id}", + visibility: Visibility::Public, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + VerifyEndpoint { url: &*DEMO_VPC_ROUTER_URL, visibility: Visibility::Protected, @@ -815,6 +829,14 @@ lazy_static! { ], }, + VerifyEndpoint { + url: "/by-id/vpc-router-routes/{id}", + visibility: Visibility::Public, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + VerifyEndpoint { url: &*DEMO_ROUTER_ROUTE_URL, visibility: Visibility::Protected, @@ -850,6 +872,14 @@ lazy_static! { ], }, + VerifyEndpoint { + url: "/by-id/disks/{id}", + visibility: Visibility::Public, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + VerifyEndpoint { url: &*DEMO_DISK_URL, visibility: Visibility::Protected, @@ -902,6 +932,14 @@ lazy_static! { ], }, + VerifyEndpoint { + url: "/by-id/images/{id}", + visibility: Visibility::Public, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + VerifyEndpoint { url: &*DEMO_PROJECT_IMAGE_URL, visibility: Visibility::Protected, @@ -923,6 +961,15 @@ lazy_static! { ) ] }, + + VerifyEndpoint { + url: "/by-id/snapshots/{id}", + visibility: Visibility::Public, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + VerifyEndpoint { url: &*DEMO_SNAPSHOT_URL, visibility: Visibility::Protected, @@ -944,6 +991,14 @@ lazy_static! { ], }, + VerifyEndpoint { + url: "/by-id/instances/{id}", + visibility: Visibility::Public, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + VerifyEndpoint { url: &*DEMO_INSTANCE_URL, visibility: Visibility::Protected, @@ -1004,6 +1059,15 @@ lazy_static! { ), ], }, + + VerifyEndpoint { + url: "/by-id/network-interfaces/{id}", + visibility: Visibility::Public, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + VerifyEndpoint { url: &*DEMO_INSTANCE_NIC_URL, visibility: Visibility::Protected, @@ -1111,6 +1175,14 @@ lazy_static! { ], }, + VerifyEndpoint { + url: "/by-id/global-images/{id}", + visibility: Visibility::Public, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + VerifyEndpoint { url: &*DEMO_GLOBAL_IMAGE_URL, visibility: Visibility::Protected, diff --git a/nexus/tests/integration_tests/unauthorized.rs b/nexus/tests/integration_tests/unauthorized.rs index 497856bacc..5933363929 100644 --- a/nexus/tests/integration_tests/unauthorized.rs +++ b/nexus/tests/integration_tests/unauthorized.rs @@ -7,7 +7,6 @@ use super::endpoints::*; use crate::integration_tests::saml::SAML_IDP_DESCRIPTOR; -use diesel::expression::AsExpression; use dropshot::test_util::ClientTestContext; use dropshot::HttpErrorResponseBody; use headers::authorization::Credentials; From 07112fd6d9a70d9a1ac6c3194a195c2ae8610393 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Thu, 7 Jul 2022 15:36:34 -0400 Subject: [PATCH 15/18] Fix comment --- nexus/src/external_api/http_entrypoints.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index dd2b1e4581..d974a1feae 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -270,7 +270,7 @@ pub fn external_api() -> NexusApiDescription { // An exception to this are id lookup operations which have a different top-level route // but will still be grouped with the collection. For example: // -// GET /by-id/organizations/{org_name} (look up a organization in the collection by id) +// GET /by-id/organizations/{id} (look up a organization in the collection by id) // // We pick a name for the function that implements a given API entrypoint // based on how we expect it to appear in the CLI subcommand hierarchy. For From 428406711ba9473e96f7f4b1e95dfeda4ddf678e Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Fri, 8 Jul 2022 18:35:28 -0400 Subject: [PATCH 16/18] Fix unauth tests --- nexus/src/app/image.rs | 9 +- nexus/tests/integration_tests/endpoints.rs | 28 ++--- nexus/tests/integration_tests/unauthorized.rs | 116 +++++++++++------- 3 files changed, 90 insertions(+), 63 deletions(-) diff --git a/nexus/src/app/image.rs b/nexus/src/app/image.rs index a26f96ae93..61a9d84093 100644 --- a/nexus/src/app/image.rs +++ b/nexus/src/app/image.rs @@ -76,11 +76,10 @@ impl super::Nexus { opctx: &OpContext, image_id: &Uuid, ) -> LookupResult { - let (.., db_image) = LookupPath::new(opctx, &self.db_datastore) - .image_id(*image_id) - .fetch() - .await?; - Ok(db_image) + let lookup_type = LookupType::ById(*image_id); + let not_found_error = lookup_type.into_not_found(ResourceType::Image); + let unimp = Unimpl::ProtectedLookup(not_found_error); + Err(self.unimplemented_todo(opctx, unimp).await) } pub async fn project_delete_image( diff --git a/nexus/tests/integration_tests/endpoints.rs b/nexus/tests/integration_tests/endpoints.rs index 50a3bf98e7..e73c69851f 100644 --- a/nexus/tests/integration_tests/endpoints.rs +++ b/nexus/tests/integration_tests/endpoints.rs @@ -582,7 +582,7 @@ lazy_static! { VerifyEndpoint { url: "/by-id/organizations/{id}", - visibility: Visibility::Public, + visibility: Visibility::Protected, allowed_methods: vec![ AllowedMethod::Get, ], @@ -643,7 +643,7 @@ lazy_static! { VerifyEndpoint { url: "/by-id/projects/{id}", - visibility: Visibility::Public, + visibility: Visibility::Protected, allowed_methods: vec![ AllowedMethod::Get, ], @@ -695,7 +695,7 @@ lazy_static! { VerifyEndpoint { url: "/by-id/vpcs/{id}", - visibility: Visibility::Public, + visibility: Visibility::Protected, allowed_methods: vec![ AllowedMethod::Get, ], @@ -747,7 +747,7 @@ lazy_static! { VerifyEndpoint { url: "/by-id/vpc-subnets/{id}", - visibility: Visibility::Public, + visibility: Visibility::Protected, allowed_methods: vec![ AllowedMethod::Get, ], @@ -793,7 +793,7 @@ lazy_static! { VerifyEndpoint { url: "/by-id/vpc-routers/{id}", - visibility: Visibility::Public, + visibility: Visibility::Protected, allowed_methods: vec![ AllowedMethod::Get, ], @@ -831,7 +831,7 @@ lazy_static! { VerifyEndpoint { url: "/by-id/vpc-router-routes/{id}", - visibility: Visibility::Public, + visibility: Visibility::Protected, allowed_methods: vec![ AllowedMethod::Get, ], @@ -874,7 +874,7 @@ lazy_static! { VerifyEndpoint { url: "/by-id/disks/{id}", - visibility: Visibility::Public, + visibility: Visibility::Protected, allowed_methods: vec![ AllowedMethod::Get, ], @@ -934,9 +934,9 @@ lazy_static! { VerifyEndpoint { url: "/by-id/images/{id}", - visibility: Visibility::Public, + visibility: Visibility::Protected, allowed_methods: vec![ - AllowedMethod::Get, + AllowedMethod::GetUnimplemented, ], }, @@ -964,9 +964,9 @@ lazy_static! { VerifyEndpoint { url: "/by-id/snapshots/{id}", - visibility: Visibility::Public, + visibility: Visibility::Protected, allowed_methods: vec![ - AllowedMethod::Get, + AllowedMethod::GetUnimplemented, ], }, @@ -993,7 +993,7 @@ lazy_static! { VerifyEndpoint { url: "/by-id/instances/{id}", - visibility: Visibility::Public, + visibility: Visibility::Protected, allowed_methods: vec![ AllowedMethod::Get, ], @@ -1062,7 +1062,7 @@ lazy_static! { VerifyEndpoint { url: "/by-id/network-interfaces/{id}", - visibility: Visibility::Public, + visibility: Visibility::Protected, allowed_methods: vec![ AllowedMethod::Get, ], @@ -1177,7 +1177,7 @@ lazy_static! { VerifyEndpoint { url: "/by-id/global-images/{id}", - visibility: Visibility::Public, + visibility: Visibility::Protected, allowed_methods: vec![ AllowedMethod::Get, ], diff --git a/nexus/tests/integration_tests/unauthorized.rs b/nexus/tests/integration_tests/unauthorized.rs index 5933363929..7fcfcf59fa 100644 --- a/nexus/tests/integration_tests/unauthorized.rs +++ b/nexus/tests/integration_tests/unauthorized.rs @@ -61,15 +61,29 @@ async fn test_unauthorized(cptestctx: &ControlPlaneTestContext) { // Create test data. info!(log, "setting up resource hierarchy"); for request in &*SETUP_REQUESTS { - let result = - NexusRequest::objects_post(client, request.url, &request.body) - .authn_as(AuthnMode::PrivilegedUser) - .execute() - .await - .unwrap(); + let (url, result, id_routes) = match request { + SetupReq::Get { url, id_routes } => ( + url, + NexusRequest::object_get(client, url) + .authn_as(AuthnMode::PrivilegedUser) + .execute() + .await + .unwrap(), + id_routes, + ), + SetupReq::Post { url, body, id_routes } => ( + url, + NexusRequest::objects_post(client, url, body) + .authn_as(AuthnMode::PrivilegedUser) + .execute() + .await + .unwrap(), + id_routes, + ), + }; - setup_results.insert(request.url, result.clone()); - request.id_routes.iter().for_each(|id_route| { + setup_results.insert(url, result.clone()); + id_routes.iter().for_each(|id_route| { setup_results.insert(id_route, result.clone()); }); } @@ -78,7 +92,8 @@ async fn test_unauthorized(cptestctx: &ControlPlaneTestContext) { info!(log, "verifying endpoints"); print!("{}", VERIFY_HEADER); for endpoint in &*VERIFY_ENDPOINTS { - verify_endpoint(&log, client, endpoint, &setup_results).await; + let setup_response = setup_results.get(&endpoint.url); + verify_endpoint(&log, client, endpoint, setup_response).await; } } @@ -122,15 +137,20 @@ G GET PUT POST DEL TRCE G URL /// Describes a request made during the setup phase to create a resource that /// we'll use later in the verification phase /// -/// The setup phase takes a list of `SetupReq` structs and issues `POST` -/// requests to each one's `url` with the specific `body`. -struct SetupReq { - /// url to send the `POST` to - url: &'static str, - /// body of the `POST` request - body: serde_json::Value, - /// populates `id` params for later route lookups - id_routes: Vec<&'static str>, +/// The setup phase takes a list of `SetupReq` enums and issues a `GET` or `POST` +/// request to each one's `url`. The `id_routes` field is a list of URLs that result +/// the result of the request to replace `id` parameters if present. + +enum SetupReq { + Get { + url: &'static str, + id_routes: Vec<&'static str>, + }, + Post { + url: &'static str, + body: serde_json::Value, + id_routes: Vec<&'static str>, + }, } lazy_static! { @@ -162,79 +182,84 @@ lazy_static! { /// List of requests to execute at setup time static ref SETUP_REQUESTS: Vec = vec![ // Create a separate Silo (not used for anything else) - SetupReq { + SetupReq::Post { url: "/silos", body: serde_json::to_value(&*DEMO_SILO_CREATE).unwrap(), id_routes: vec!["/by-id/silos/{id}"], }, // Create an IP pool - SetupReq { + SetupReq::Post { url: &*DEMO_IP_POOLS_URL, body: serde_json::to_value(&*DEMO_IP_POOL_CREATE).unwrap(), id_routes: vec!["/by-id/ip-pools/{id}"], }, // Create an IP Pool range - SetupReq { + SetupReq::Post { url: &*DEMO_IP_POOL_RANGES_ADD_URL, body: serde_json::to_value(&*DEMO_IP_POOL_RANGE).unwrap(), id_routes: vec![], }, // Create an Organization - SetupReq { + SetupReq::Post { url: "/organizations", body: serde_json::to_value(&*DEMO_ORG_CREATE).unwrap(), id_routes: vec!["/by-id/organizations/{id}"], }, // Create a Project in the Organization - SetupReq { + SetupReq::Post { url: &*DEMO_ORG_PROJECTS_URL, body: serde_json::to_value(&*DEMO_PROJECT_CREATE).unwrap(), id_routes: vec!["/by-id/projects/{id}"], }, // Create a VPC in the Project - SetupReq { + SetupReq::Post { url: &*DEMO_PROJECT_URL_VPCS, body: serde_json::to_value(&*DEMO_VPC_CREATE).unwrap(), id_routes: vec!["/by-id/vpcs/{id}"], }, // Create a VPC Subnet in the Vpc - SetupReq { + SetupReq::Post { url: &*DEMO_VPC_URL_SUBNETS, body: serde_json::to_value(&*DEMO_VPC_SUBNET_CREATE).unwrap(), id_routes: vec!["/by-id/vpc-subnets/{id}"], }, // Create a VPC Router in the Vpc - SetupReq { + SetupReq::Post { url: &*DEMO_VPC_URL_ROUTERS, body: serde_json::to_value(&*DEMO_VPC_ROUTER_CREATE).unwrap(), id_routes: vec!["/by-id/vpc-routers/{id}"], }, // Create a VPC Router in the Vpc - SetupReq { + SetupReq::Post { url: &*DEMO_VPC_ROUTER_URL_ROUTES, body: serde_json::to_value(&*DEMO_ROUTER_ROUTE_CREATE).unwrap(), id_routes: vec!["/by-id/vpc-router-routes/{id}"], }, // Create a Disk in the Project - SetupReq { + SetupReq::Post { url: &*DEMO_PROJECT_URL_DISKS, body: serde_json::to_value(&*DEMO_DISK_CREATE).unwrap(), id_routes: vec!["/by-id/disks/{id}"], }, // Create an Instance in the Project - SetupReq { + SetupReq::Post { url: &*DEMO_PROJECT_URL_INSTANCES, body: serde_json::to_value(&*DEMO_INSTANCE_CREATE).unwrap(), id_routes: vec!["/by-id/instances/{id}"], }, + // Lookup the previously created NIC + SetupReq::Get { + url: &*DEMO_INSTANCE_NIC_URL, + id_routes: vec!["/by-id/network-interfaces/{id}"], + }, // Create a GlobalImage - SetupReq { + SetupReq::Post { url: "/images", body: serde_json::to_value(&*DEMO_GLOBAL_IMAGE_CREATE).unwrap(), id_routes: vec!["/by-id/global-images/{id}"], }, // Create a SAML identity provider - SetupReq { + SetupReq::Post { url: &*SAML_IDENTITY_PROVIDERS_URL, body: serde_json::to_value(&*SAML_IDENTITY_PROVIDER).unwrap(), id_routes: vec![], @@ -296,7 +321,7 @@ async fn verify_endpoint( log: &slog::Logger, client: &ClientTestContext, endpoint: &VerifyEndpoint, - setup_results: &std::collections::BTreeMap<&str, TestResponse>, + setup_response: Option<&TestResponse>, ) { let log = log.new(o!("url" => endpoint.url)); info!(log, "test: begin endpoint"); @@ -312,19 +337,22 @@ async fn verify_endpoint( Visibility::Protected => StatusCode::NOT_FOUND, }; - // For routes with an id param, find their matching id in the setup results. + // For routes with an id param, replace the id param with the setup response if present. let uri = if endpoint.url.contains("{id}") { - endpoint.url.replace( - "{id}", - &setup_results - .get(endpoint.url) - .unwrap() - .parsed_body::() - .unwrap() - .id - .to_string() - .as_str(), - ) + match setup_response { + Some(response) => endpoint.url.replace( + "{id}", + response + .parsed_body::() + .unwrap() + .id + .to_string() + .as_str(), + ), + None => endpoint + .url + .replace("{id}", "00000000-0000-0000-0000-000000000000"), + } } else { endpoint.url.to_string() }; From 39c658b4bb4786b87d1f53d43b565b784ccf4136 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Mon, 11 Jul 2022 19:39:37 -0400 Subject: [PATCH 17/18] Restore test that was added by @ahl in #751 --- nexus/src/external_api/http_entrypoints.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index d974a1feae..85f7c7158d 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -4044,3 +4044,15 @@ async fn session_sshkey_delete( }; apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } + +#[cfg(test)] +mod test { + use super::external_api; + + #[test] + fn test_nexus_tag_policy() { + // This will fail if any of the endpoints don't match the policy in + // ./tag-config.json + let _ = external_api(); + } +} From 67e1c8d297f92c4fb0e81042b485ba1dc5dc4901 Mon Sep 17 00:00:00 2001 From: Justin Bennett Date: Mon, 11 Jul 2022 19:45:27 -0400 Subject: [PATCH 18/18] Improve the setup req comment --- nexus/tests/integration_tests/unauthorized.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nexus/tests/integration_tests/unauthorized.rs b/nexus/tests/integration_tests/unauthorized.rs index 7fcfcf59fa..32504da9cf 100644 --- a/nexus/tests/integration_tests/unauthorized.rs +++ b/nexus/tests/integration_tests/unauthorized.rs @@ -138,8 +138,10 @@ G GET PUT POST DEL TRCE G URL /// we'll use later in the verification phase /// /// The setup phase takes a list of `SetupReq` enums and issues a `GET` or `POST` -/// request to each one's `url`. The `id_routes` field is a list of URLs that result -/// the result of the request to replace `id` parameters if present. +/// request to each one's `url`. `id_results` is a list of URLs that are associated +/// to the results of the setup request with any `{id}` params in the URL replaced with +/// the result's URL. This is used to later verify ID endpoints without first having to +/// know the ID. enum SetupReq { Get {