diff --git a/end-to-end-tests/src/bin/bootstrap.rs b/end-to-end-tests/src/bin/bootstrap.rs index 21e59647ae..b02bed4265 100644 --- a/end-to-end-tests/src/bin/bootstrap.rs +++ b/end-to-end-tests/src/bin/bootstrap.rs @@ -4,7 +4,7 @@ use end_to_end_tests::helpers::{generate_name, get_system_ip_pool}; use omicron_test_utils::dev::poll::{wait_for_condition, CondCheckError}; use oxide_client::types::{ ByteCount, DeviceAccessTokenRequest, DeviceAuthRequest, DeviceAuthVerify, - DiskCreate, DiskSource, IpPoolCreate, IpPoolSiloLink, IpRange, Ipv4Range, + DiskCreate, DiskSource, IpPoolCreate, IpPoolLinkSilo, IpRange, Ipv4Range, NameOrId, SiloQuotasUpdate, }; use oxide_client::{ @@ -51,7 +51,7 @@ async fn main() -> Result<()> { client .ip_pool_silo_link() .pool(pool_name) - .body(IpPoolSiloLink { + .body(IpPoolLinkSilo { silo: NameOrId::Name(params.silo_name().parse().unwrap()), is_default: true, }) diff --git a/nexus/db-model/src/ip_pool.rs b/nexus/db-model/src/ip_pool.rs index bec1113151..030d052c22 100644 --- a/nexus/db-model/src/ip_pool.rs +++ b/nexus/db-model/src/ip_pool.rs @@ -97,7 +97,7 @@ pub struct IpPoolResource { pub is_default: bool, } -impl From for views::IpPoolSilo { +impl From for views::IpPoolSiloLink { fn from(assoc: IpPoolResource) -> Self { Self { ip_pool_id: assoc.ip_pool_id, diff --git a/nexus/src/app/ip_pool.rs b/nexus/src/app/ip_pool.rs index 902b3c97e8..b5648a3346 100644 --- a/nexus/src/app/ip_pool.rs +++ b/nexus/src/app/ip_pool.rs @@ -128,7 +128,7 @@ impl super::Nexus { &self, opctx: &OpContext, pool_lookup: &lookup::IpPool<'_>, - silo_link: ¶ms::IpPoolSiloLink, + silo_link: ¶ms::IpPoolLinkSilo, ) -> CreateResult { let (authz_pool,) = pool_lookup.lookup_for(authz::Action::Modify).await?; diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index 81117d7429..65b03a9fdf 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -1546,7 +1546,7 @@ async fn ip_pool_silo_list( // whatever the thing is. Still... all we'd have to do to make this usable // in both places would be to make it { ...IpPool, silo_id, silo_name, // is_default } -) -> Result>, HttpError> { +) -> Result>, HttpError> { let apictx = rqctx.context(); let handler = async { let opctx = crate::context::op_context_for_external_api(&rqctx).await?; @@ -1568,7 +1568,7 @@ async fn ip_pool_silo_list( Ok(HttpResponseOk(ScanById::results_page( &query, assocs, - &|_, x: &views::IpPoolSilo| x.silo_id, + &|_, x: &views::IpPoolSiloLink| x.silo_id, )?)) }; apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await @@ -1583,8 +1583,8 @@ async fn ip_pool_silo_list( async fn ip_pool_silo_link( rqctx: RequestContext>, path_params: Path, - resource_assoc: TypedBody, -) -> Result, HttpError> { + resource_assoc: TypedBody, +) -> Result, HttpError> { let apictx = rqctx.context(); let handler = async { let opctx = crate::context::op_context_for_external_api(&rqctx).await?; @@ -1638,7 +1638,7 @@ async fn ip_pool_silo_update( rqctx: RequestContext>, path_params: Path, update: TypedBody, -) -> Result, HttpError> { +) -> Result, HttpError> { let apictx = rqctx.context(); let handler = async { let opctx = crate::context::op_context_for_external_api(&rqctx).await?; diff --git a/nexus/test-utils/src/resource_helpers.rs b/nexus/test-utils/src/resource_helpers.rs index c2516a1509..4fe03f204c 100644 --- a/nexus/test-utils/src/resource_helpers.rs +++ b/nexus/test-utils/src/resource_helpers.rs @@ -246,9 +246,9 @@ pub async fn link_ip_pool( is_default: bool, ) { let link = - params::IpPoolSiloLink { silo: NameOrId::Id(*silo_id), is_default }; + params::IpPoolLinkSilo { silo: NameOrId::Id(*silo_id), is_default }; let url = format!("/v1/system/ip-pools/{pool_name}/silos"); - object_create::( + object_create::( client, &url, &link, ) .await; diff --git a/nexus/tests/integration_tests/endpoints.rs b/nexus/tests/integration_tests/endpoints.rs index e8d54b876e..8beffe43a5 100644 --- a/nexus/tests/integration_tests/endpoints.rs +++ b/nexus/tests/integration_tests/endpoints.rs @@ -629,8 +629,8 @@ pub static DEMO_IP_POOL_UPDATE: Lazy = }); pub static DEMO_IP_POOL_SILOS_URL: Lazy = Lazy::new(|| format!("{}/silos", *DEMO_IP_POOL_URL)); -pub static DEMO_IP_POOL_SILOS_BODY: Lazy = - Lazy::new(|| params::IpPoolSiloLink { +pub static DEMO_IP_POOL_SILOS_BODY: Lazy = + Lazy::new(|| params::IpPoolLinkSilo { silo: NameOrId::Id(DEFAULT_SILO.identity().id), is_default: true, // necessary for demo instance create to go through }); diff --git a/nexus/tests/integration_tests/instances.rs b/nexus/tests/integration_tests/instances.rs index 044f87f7c1..2f4e913185 100644 --- a/nexus/tests/integration_tests/instances.rs +++ b/nexus/tests/integration_tests/instances.rs @@ -3657,7 +3657,7 @@ async fn test_instance_ephemeral_ip_from_correct_pool( ); // make pool2 default and create instance with default pool. check that it now it comes from pool2 - let _: views::IpPoolSilo = object_put( + let _: views::IpPoolSiloLink = object_put( client, &format!("/v1/system/ip-pools/pool2/silos/{}", DEFAULT_SILO.id()), ¶ms::IpPoolSiloUpdate { is_default: true }, @@ -3788,11 +3788,11 @@ async fn test_instance_ephemeral_ip_from_orphan_pool( // associate the pool with a different silo and we should get the same // error on instance create - let params = params::IpPoolSiloLink { + let params = params::IpPoolLinkSilo { silo: NameOrId::Name(cptestctx.silo_name.clone()), is_default: false, }; - let _: views::IpPoolSilo = + let _: views::IpPoolSiloLink = object_create(client, "/v1/system/ip-pools/orphan-pool/silos", ¶ms) .await; diff --git a/nexus/tests/integration_tests/ip_pools.rs b/nexus/tests/integration_tests/ip_pools.rs index 9a261e9e8d..7843e816fd 100644 --- a/nexus/tests/integration_tests/ip_pools.rs +++ b/nexus/tests/integration_tests/ip_pools.rs @@ -31,7 +31,7 @@ use nexus_test_utils::resource_helpers::objects_list_page_authz; use nexus_test_utils_macros::nexus_test; use nexus_types::external_api::params; use nexus_types::external_api::params::IpPoolCreate; -use nexus_types::external_api::params::IpPoolSiloLink; +use nexus_types::external_api::params::IpPoolLinkSilo; use nexus_types::external_api::params::IpPoolSiloUpdate; use nexus_types::external_api::params::IpPoolUpdate; use nexus_types::external_api::shared::IpRange; @@ -40,7 +40,7 @@ use nexus_types::external_api::shared::Ipv6Range; use nexus_types::external_api::shared::SiloIdentityMode; use nexus_types::external_api::views::IpPool; use nexus_types::external_api::views::IpPoolRange; -use nexus_types::external_api::views::IpPoolSilo; +use nexus_types::external_api::views::IpPoolSiloLink; use nexus_types::external_api::views::Silo; use nexus_types::external_api::views::SiloIpPool; use nexus_types::identity::Resource; @@ -346,7 +346,7 @@ async fn test_ip_pool_service_no_cud(cptestctx: &ControlPlaneTestContext) { // linking not allowed - // let link_body = params::IpPoolSiloLink { + // let link_body = params::IpPoolLinkSilo { // silo: NameOrId::Name(cptestctx.silo_name.clone()), // is_default: false, // }; @@ -380,7 +380,7 @@ async fn test_ip_pool_silo_link(cptestctx: &ControlPlaneTestContext) { // expect 404 on association if the specified silo doesn't exist let nonexistent_silo_id = Uuid::new_v4(); - let params = params::IpPoolSiloLink { + let params = params::IpPoolLinkSilo { silo: NameOrId::Id(nonexistent_silo_id), is_default: false, }; @@ -404,8 +404,8 @@ async fn test_ip_pool_silo_link(cptestctx: &ControlPlaneTestContext) { // associate by name with silo that exists let silo = NameOrId::Name(cptestctx.silo_name.clone()); let params = - params::IpPoolSiloLink { silo: silo.clone(), is_default: false }; - let _: IpPoolSilo = + params::IpPoolLinkSilo { silo: silo.clone(), is_default: false }; + let _: IpPoolSiloLink = object_create(client, "/v1/system/ip-pools/p0/silos", ¶ms).await; // second attempt to create the same link errors due to conflict @@ -423,8 +423,11 @@ async fn test_ip_pool_silo_link(cptestctx: &ControlPlaneTestContext) { let silo_id = object_get::(client, &silo_url).await.identity.id; let assocs_p0 = silos_for_pool(client, "p0").await; - let silo_link = - IpPoolSilo { ip_pool_id: p0.identity.id, silo_id, is_default: false }; + let silo_link = IpPoolSiloLink { + ip_pool_id: p0.identity.id, + silo_id, + is_default: false, + }; assert_eq!(assocs_p0.items.len(), 1); assert_eq!(assocs_p0.items[0], silo_link); @@ -434,18 +437,22 @@ async fn test_ip_pool_silo_link(cptestctx: &ControlPlaneTestContext) { assert_eq!(silo_pools[0].is_default, false); // associate same silo to other pool by ID instead of name - let link_params = params::IpPoolSiloLink { + let link_params = params::IpPoolLinkSilo { silo: NameOrId::Id(silo_id), is_default: true, }; let url = "/v1/system/ip-pools/p1/silos"; - let _: IpPoolSilo = object_create(client, &url, &link_params).await; + let _: IpPoolSiloLink = object_create(client, &url, &link_params).await; let silos_p1 = silos_for_pool(client, "p1").await; assert_eq!(silos_p1.items.len(), 1); assert_eq!( silos_p1.items[0], - IpPoolSilo { ip_pool_id: p1.identity.id, is_default: true, silo_id } + IpPoolSiloLink { + ip_pool_id: p1.identity.id, + is_default: true, + silo_id + } ); let silo_pools = pools_for_silo(client, silo_name).await; @@ -525,10 +532,10 @@ async fn test_ip_pool_update_default(cptestctx: &ControlPlaneTestContext) { // associate both pools with the test silo let silo = NameOrId::Name(cptestctx.silo_name.clone()); let params = - params::IpPoolSiloLink { silo: silo.clone(), is_default: false }; - let _: IpPoolSilo = + params::IpPoolLinkSilo { silo: silo.clone(), is_default: false }; + let _: IpPoolSiloLink = object_create(client, "/v1/system/ip-pools/p0/silos", ¶ms).await; - let _: IpPoolSilo = + let _: IpPoolSiloLink = object_create(client, "/v1/system/ip-pools/p1/silos", ¶ms).await; // now both are linked to the silo, neither is marked default @@ -542,10 +549,10 @@ async fn test_ip_pool_update_default(cptestctx: &ControlPlaneTestContext) { // make p0 default let params = IpPoolSiloUpdate { is_default: true }; - let _: IpPoolSilo = object_put(client, &p0_silo_url, ¶ms).await; + let _: IpPoolSiloLink = object_put(client, &p0_silo_url, ¶ms).await; // making the same one default again is not an error - let _: IpPoolSilo = object_put(client, &p0_silo_url, ¶ms).await; + let _: IpPoolSiloLink = object_put(client, &p0_silo_url, ¶ms).await; // now p0 is default let silos_p0 = silos_for_pool(client, "p0").await; @@ -563,7 +570,7 @@ async fn test_ip_pool_update_default(cptestctx: &ControlPlaneTestContext) { let params = IpPoolSiloUpdate { is_default: true }; let p1_silo_url = format!("/v1/system/ip-pools/p1/silos/{}", cptestctx.silo_name); - let _: IpPoolSilo = object_put(client, &p1_silo_url, ¶ms).await; + let _: IpPoolSiloLink = object_put(client, &p1_silo_url, ¶ms).await; // p1 is now default let silos_p1 = silos_for_pool(client, "p1").await; @@ -577,7 +584,7 @@ async fn test_ip_pool_update_default(cptestctx: &ControlPlaneTestContext) { // we can also unset default let params = IpPoolSiloUpdate { is_default: false }; - let _: IpPoolSilo = object_put(client, &p1_silo_url, ¶ms).await; + let _: IpPoolSiloLink = object_put(client, &p1_silo_url, ¶ms).await; let silos_p1 = silos_for_pool(client, "p1").await; assert_eq!(silos_p1.items.len(), 1); @@ -629,9 +636,9 @@ fn get_names(pools: Vec) -> Vec { async fn silos_for_pool( client: &ClientTestContext, pool: &str, -) -> ResultsPage { +) -> ResultsPage { let url = format!("/v1/system/ip-pools/{}/silos", pool); - objects_list_page_authz::(client, &url).await + objects_list_page_authz::(client, &url).await } async fn pools_for_silo( @@ -1028,13 +1035,13 @@ async fn test_ip_range_delete_with_allocated_external_ip_fails( .await; // associate pool with default silo, which is the privileged user's silo - let params = IpPoolSiloLink { + let params = IpPoolLinkSilo { silo: NameOrId::Id(DEFAULT_SILO.id()), is_default: true, }; NexusRequest::objects_post(client, &ip_pool_silos_url, ¶ms) .authn_as(AuthnMode::PrivilegedUser) - .execute_and_parse_unwrap::() + .execute_and_parse_unwrap::() .await; // Add an IP range to the pool diff --git a/nexus/types/src/external_api/params.rs b/nexus/types/src/external_api/params.rs index a33bc0b8bb..750e83c2a2 100644 --- a/nexus/types/src/external_api/params.rs +++ b/nexus/types/src/external_api/params.rs @@ -855,7 +855,7 @@ pub struct IpPoolSiloPath { } #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] -pub struct IpPoolSiloLink { +pub struct IpPoolLinkSilo { pub silo: NameOrId, /// When a pool is the default for a silo, floating IPs and instance /// ephemeral IPs will come from that pool when no other pool is specified. diff --git a/nexus/types/src/external_api/views.rs b/nexus/types/src/external_api/views.rs index b39a7031b6..314dd4ed00 100644 --- a/nexus/types/src/external_api/views.rs +++ b/nexus/types/src/external_api/views.rs @@ -315,13 +315,10 @@ pub struct SiloIpPool { pub is_default: bool, } -// TODO: rename IpPoolSilo or get rid of it somehow. we cannot have both -// IpPoolSilo and SiloIpPool. come on - /// A link between an IP pool and a silo that allows one to allocate IPs from /// the pool within the silo #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq)] -pub struct IpPoolSilo { +pub struct IpPoolSiloLink { pub ip_pool_id: Uuid, pub silo_id: Uuid, /// When a pool is the default for a silo, floating IPs and instance diff --git a/openapi/nexus.json b/openapi/nexus.json index 3551c450ce..2dd4037430 100644 --- a/openapi/nexus.json +++ b/openapi/nexus.json @@ -5039,7 +5039,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IpPoolSiloResultsPage" + "$ref": "#/components/schemas/IpPoolSiloLinkResultsPage" } } } @@ -5076,7 +5076,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IpPoolSiloLink" + "$ref": "#/components/schemas/IpPoolLinkSilo" } } }, @@ -5088,7 +5088,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IpPoolSilo" + "$ref": "#/components/schemas/IpPoolSiloLink" } } } @@ -5144,7 +5144,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IpPoolSilo" + "$ref": "#/components/schemas/IpPoolSiloLink" } } } @@ -6684,7 +6684,7 @@ "in": "query", "name": "sort_by", "schema": { - "$ref": "#/components/schemas/IdSortMode" + "$ref": "#/components/schemas/NameOrIdSortMode" } } ], @@ -12565,6 +12565,22 @@ "name" ] }, + "IpPoolLinkSilo": { + "type": "object", + "properties": { + "is_default": { + "description": "When a pool is the default for a silo, floating IPs and instance ephemeral IPs will come from that pool when no other pool is specified. There can be at most one default for a given silo.", + "type": "boolean" + }, + "silo": { + "$ref": "#/components/schemas/NameOrId" + } + }, + "required": [ + "is_default", + "silo" + ] + }, "IpPoolRange": { "type": "object", "properties": { @@ -12633,7 +12649,7 @@ "items" ] }, - "IpPoolSilo": { + "IpPoolSiloLink": { "description": "A link between an IP pool and a silo that allows one to allocate IPs from the pool within the silo", "type": "object", "properties": { @@ -12656,23 +12672,7 @@ "silo_id" ] }, - "IpPoolSiloLink": { - "type": "object", - "properties": { - "is_default": { - "description": "When a pool is the default for a silo, floating IPs and instance ephemeral IPs will come from that pool when no other pool is specified. There can be at most one default for a given silo.", - "type": "boolean" - }, - "silo": { - "$ref": "#/components/schemas/NameOrId" - } - }, - "required": [ - "is_default", - "silo" - ] - }, - "IpPoolSiloResultsPage": { + "IpPoolSiloLinkResultsPage": { "description": "A single page of results", "type": "object", "properties": { @@ -12680,7 +12680,7 @@ "description": "list of items on this page of results", "type": "array", "items": { - "$ref": "#/components/schemas/IpPoolSilo" + "$ref": "#/components/schemas/IpPoolSiloLink" } }, "next_page": {