Skip to content

Commit dfac975

Browse files
authored
[nexus] Add address lot view method. (#8752)
Part of oxidecomputer/terraform-provider-oxide#468. The terraform resource expects to be able to create/read/delete address lots, and so far nexus only supports create/list/delete. This patch adds a new endpoint for viewing an address lot by name or id. This is my first attempt to work on this repo, so let me know if this is off-base @internet-diglett.
1 parent d8f30c1 commit dfac975

File tree

9 files changed

+148
-6
lines changed

9 files changed

+148
-6
lines changed

common/src/api/external/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2588,6 +2588,16 @@ pub struct AddressLotCreateResponse {
25882588
pub blocks: Vec<AddressLotBlock>,
25892589
}
25902590

2591+
/// An address lot and associated blocks resulting from viewing an address lot.
2592+
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
2593+
pub struct AddressLotViewResponse {
2594+
/// The address lot.
2595+
pub lot: AddressLot,
2596+
2597+
/// The address lot blocks.
2598+
pub blocks: Vec<AddressLotBlock>,
2599+
}
2600+
25912601
/// Represents an address lot object, containing the id of the lot that can be
25922602
/// used in other API calls.
25932603
// TODO Add kind attribute to AddressLot

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,13 +248,18 @@ impl DataStore {
248248
&self,
249249
opctx: &OpContext,
250250
authz_address_lot: &authz::AddressLot,
251-
pagparams: &DataPageParams<'_, Uuid>,
251+
pagparams: Option<&DataPageParams<'_, Uuid>>,
252252
) -> ListResultVec<AddressLotBlock> {
253253
use nexus_db_schema::schema::address_lot_block::dsl;
254254

255255
let conn = self.pool_connection_authorized(opctx).await?;
256256

257-
paginated(dsl::address_lot_block, dsl::id, &pagparams)
257+
let table = match pagparams {
258+
Some(params) => paginated(dsl::address_lot_block, dsl::id, &params),
259+
None => dsl::address_lot_block.into_boxed(),
260+
};
261+
262+
table
258263
.filter(dsl::address_lot_id.eq(authz_address_lot.id()))
259264
.select(AddressLotBlock::as_select())
260265
.load_async(&*conn)

nexus/external-api/output/nexus_tags.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ networking_address_lot_block_list GET /v1/system/networking/address-
243243
networking_address_lot_create POST /v1/system/networking/address-lot
244244
networking_address_lot_delete DELETE /v1/system/networking/address-lot/{address_lot}
245245
networking_address_lot_list GET /v1/system/networking/address-lot
246+
networking_address_lot_view GET /v1/system/networking/address-lot/{address_lot}
246247
networking_allow_list_update PUT /v1/system/networking/allow-list
247248
networking_allow_list_view GET /v1/system/networking/allow-list
248249
networking_bfd_disable POST /v1/system/networking/bfd-disable

nexus/external-api/src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,6 +1630,17 @@ pub trait NexusExternalApi {
16301630
query_params: Query<PaginatedByNameOrId>,
16311631
) -> Result<HttpResponseOk<ResultsPage<AddressLot>>, HttpError>;
16321632

1633+
/// Fetch address lot
1634+
#[endpoint {
1635+
method = GET,
1636+
path = "/v1/system/networking/address-lot/{address_lot}",
1637+
tags = ["system/networking"],
1638+
}]
1639+
async fn networking_address_lot_view(
1640+
rqctx: RequestContext<Self::Context>,
1641+
path_params: Path<params::AddressLotPath>,
1642+
) -> Result<HttpResponseOk<AddressLotViewResponse>, HttpError>;
1643+
16331644
/// List blocks in address lot
16341645
#[endpoint {
16351646
method = GET,

nexus/src/app/address_lot.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ impl super::Nexus {
7474
self: &Arc<Self>,
7575
opctx: &OpContext,
7676
address_lot: &lookup::AddressLot<'_>,
77-
pagparams: &DataPageParams<'_, Uuid>,
77+
pagparams: Option<&DataPageParams<'_, Uuid>>,
7878
) -> ListResultVec<AddressLotBlock> {
7979
let (.., authz_address_lot) =
8080
address_lot.lookup_for(authz::Action::ListChildren).await?;

nexus/src/external_api/http_entrypoints.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ use nexus_types::{
5858
use omicron_common::api::external::AddressLot;
5959
use omicron_common::api::external::AddressLotBlock;
6060
use omicron_common::api::external::AddressLotCreateResponse;
61+
use omicron_common::api::external::AddressLotViewResponse;
6162
use omicron_common::api::external::AffinityGroupMember;
6263
use omicron_common::api::external::AggregateBgpMessageHistory;
6364
use omicron_common::api::external::AntiAffinityGroupMember;
@@ -3479,6 +3480,36 @@ impl NexusExternalApi for NexusExternalApiImpl {
34793480
.await
34803481
}
34813482

3483+
async fn networking_address_lot_view(
3484+
rqctx: RequestContext<ApiContext>,
3485+
path_params: Path<params::AddressLotPath>,
3486+
) -> Result<HttpResponseOk<AddressLotViewResponse>, HttpError> {
3487+
let apictx = rqctx.context();
3488+
let handler = async {
3489+
let opctx =
3490+
crate::context::op_context_for_external_api(&rqctx).await?;
3491+
let nexus = &apictx.context.nexus;
3492+
let path = path_params.into_inner();
3493+
let lookup = nexus.address_lot_lookup(&opctx, path.address_lot)?;
3494+
let (.., lot) = lookup.fetch().await?;
3495+
let blocks = nexus
3496+
.address_lot_block_list(&opctx, &lookup, None)
3497+
.await?
3498+
.into_iter()
3499+
.map(|p| p.into())
3500+
.collect();
3501+
Ok(HttpResponseOk(AddressLotViewResponse {
3502+
lot: lot.into(),
3503+
blocks,
3504+
}))
3505+
};
3506+
apictx
3507+
.context
3508+
.external_latencies
3509+
.instrument_dropshot_handler(&rqctx, handler)
3510+
.await
3511+
}
3512+
34823513
async fn networking_address_lot_delete(
34833514
rqctx: RequestContext<ApiContext>,
34843515
path_params: Path<params::AddressLotPath>,
@@ -3550,7 +3581,11 @@ impl NexusExternalApi for NexusExternalApiImpl {
35503581
let address_lot_lookup =
35513582
nexus.address_lot_lookup(&opctx, path.address_lot)?;
35523583
let blocks = nexus
3553-
.address_lot_block_list(&opctx, &address_lot_lookup, &pagparams)
3584+
.address_lot_block_list(
3585+
&opctx,
3586+
&address_lot_lookup,
3587+
Some(&pagparams),
3588+
)
35543589
.await?
35553590
.into_iter()
35563591
.map(|p| p.into())

nexus/tests/integration_tests/address_lots.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use nexus_types::external_api::params::{
1515
};
1616
use omicron_common::api::external::{
1717
AddressLot, AddressLotBlock, AddressLotCreateResponse, AddressLotKind,
18-
IdentityMetadataCreateParams,
18+
AddressLotViewResponse, IdentityMetadataCreateParams,
1919
};
2020
use std::net::IpAddr;
2121

@@ -78,6 +78,25 @@ async fn test_address_lot_basic_crud(ctx: &ControlPlaneTestContext) {
7878
"203.0.113.20".parse::<IpAddr>().unwrap()
7979
);
8080

81+
// View a single lot by name
82+
let view_lot = NexusRequest::object_get(
83+
client,
84+
"/v1/system/networking/address-lot/parkinglot",
85+
)
86+
.authn_as(AuthnMode::PrivilegedUser)
87+
.execute_and_parse_unwrap::<AddressLotViewResponse>()
88+
.await;
89+
assert_eq!(view_lot.lot.identity.name, "parkinglot");
90+
assert_eq!(view_lot.blocks.len(), params.blocks.len());
91+
assert_eq!(
92+
view_lot.blocks[0].first_address,
93+
"203.0.113.10".parse::<IpAddr>().unwrap()
94+
);
95+
assert_eq!(
96+
view_lot.blocks[0].last_address,
97+
"203.0.113.20".parse::<IpAddr>().unwrap()
98+
);
99+
81100
// Verify there are lots
82101
let lots = NexusRequest::iter_collection_authn::<AddressLot>(
83102
client,

nexus/tests/integration_tests/endpoints.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2738,7 +2738,7 @@ pub static VERIFY_ENDPOINTS: LazyLock<Vec<VerifyEndpoint>> = LazyLock::new(
27382738
url: &DEMO_ADDRESS_LOT_URL,
27392739
visibility: Visibility::Protected,
27402740
unprivileged_access: UnprivilegedAccess::None,
2741-
allowed_methods: vec![AllowedMethod::Delete],
2741+
allowed_methods: vec![AllowedMethod::GetNonexistent, AllowedMethod::Delete],
27422742
},
27432743
VerifyEndpoint {
27442744
url: &DEMO_ADDRESS_LOT_BLOCKS_URL,

openapi/nexus.json

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9187,6 +9187,42 @@
91879187
}
91889188
},
91899189
"/v1/system/networking/address-lot/{address_lot}": {
9190+
"get": {
9191+
"tags": [
9192+
"system/networking"
9193+
],
9194+
"summary": "Fetch address lot",
9195+
"operationId": "networking_address_lot_view",
9196+
"parameters": [
9197+
{
9198+
"in": "path",
9199+
"name": "address_lot",
9200+
"description": "Name or ID of the address lot",
9201+
"required": true,
9202+
"schema": {
9203+
"$ref": "#/components/schemas/NameOrId"
9204+
}
9205+
}
9206+
],
9207+
"responses": {
9208+
"200": {
9209+
"description": "successful operation",
9210+
"content": {
9211+
"application/json": {
9212+
"schema": {
9213+
"$ref": "#/components/schemas/AddressLotViewResponse"
9214+
}
9215+
}
9216+
}
9217+
},
9218+
"4XX": {
9219+
"$ref": "#/components/responses/Error"
9220+
},
9221+
"5XX": {
9222+
"$ref": "#/components/responses/Error"
9223+
}
9224+
}
9225+
},
91909226
"delete": {
91919227
"tags": [
91929228
"system/networking"
@@ -13671,6 +13707,31 @@
1367113707
"items"
1367213708
]
1367313709
},
13710+
"AddressLotViewResponse": {
13711+
"description": "An address lot and associated blocks resulting from viewing an address lot.",
13712+
"type": "object",
13713+
"properties": {
13714+
"blocks": {
13715+
"description": "The address lot blocks.",
13716+
"type": "array",
13717+
"items": {
13718+
"$ref": "#/components/schemas/AddressLotBlock"
13719+
}
13720+
},
13721+
"lot": {
13722+
"description": "The address lot.",
13723+
"allOf": [
13724+
{
13725+
"$ref": "#/components/schemas/AddressLot"
13726+
}
13727+
]
13728+
}
13729+
},
13730+
"required": [
13731+
"blocks",
13732+
"lot"
13733+
]
13734+
},
1367413735
"AffinityGroup": {
1367513736
"description": "View of an Affinity Group",
1367613737
"type": "object",

0 commit comments

Comments
 (0)