@@ -17,6 +17,7 @@ use nexus_test_utils::http_testing::RequestBuilder;
1717use nexus_test_utils:: resource_helpers:: create_instance;
1818use nexus_test_utils:: resource_helpers:: create_ip_pool;
1919use nexus_test_utils:: resource_helpers:: create_project;
20+ use nexus_test_utils:: resource_helpers:: create_silo;
2021use nexus_test_utils:: resource_helpers:: link_ip_pool;
2122use nexus_test_utils:: resource_helpers:: object_create;
2223use nexus_test_utils:: resource_helpers:: object_create_error;
@@ -36,13 +37,15 @@ use nexus_types::external_api::params::IpPoolUpdate;
3637use nexus_types:: external_api:: shared:: IpRange ;
3738use nexus_types:: external_api:: shared:: Ipv4Range ;
3839use nexus_types:: external_api:: shared:: Ipv6Range ;
40+ use nexus_types:: external_api:: shared:: SiloIdentityMode ;
3941use nexus_types:: external_api:: views:: IpPool ;
4042use nexus_types:: external_api:: views:: IpPoolRange ;
4143use nexus_types:: external_api:: views:: IpPoolSilo ;
4244use nexus_types:: external_api:: views:: Silo ;
4345use nexus_types:: identity:: Resource ;
4446use omicron_common:: api:: external:: IdentityMetadataUpdateParams ;
4547use omicron_common:: api:: external:: NameOrId ;
48+ use omicron_common:: api:: external:: SimpleIdentity ;
4649use omicron_common:: api:: external:: { IdentityMetadataCreateParams , Name } ;
4750use omicron_nexus:: TestInterfaces ;
4851use sled_agent_client:: TestInterfaces as SledTestInterfaces ;
@@ -62,16 +65,7 @@ async fn test_ip_pool_basic_crud(cptestctx: &ControlPlaneTestContext) {
6265 let ip_pool_ranges_url = format ! ( "{}/ranges" , ip_pool_url) ;
6366 let ip_pool_add_range_url = format ! ( "{}/add" , ip_pool_ranges_url) ;
6467
65- // Verify the list of IP pools is empty
66- let ip_pools = NexusRequest :: iter_collection_authn :: < IpPool > (
67- client,
68- ip_pools_url,
69- "" ,
70- None ,
71- )
72- . await
73- . expect ( "Failed to list IP Pools" )
74- . all_items ;
68+ let ip_pools = get_ip_pools ( & client) . await ;
7569 assert_eq ! ( ip_pools. len( ) , 0 , "Expected empty list of IP pools" ) ;
7670
7771 // Verify 404 if the pool doesn't exist yet, both for creating or deleting
@@ -102,15 +96,7 @@ async fn test_ip_pool_basic_crud(cptestctx: &ControlPlaneTestContext) {
10296 assert_eq ! ( created_pool. identity. name, pool_name) ;
10397 assert_eq ! ( created_pool. identity. description, description) ;
10498
105- let list = NexusRequest :: iter_collection_authn :: < IpPool > (
106- client,
107- ip_pools_url,
108- "" ,
109- None ,
110- )
111- . await
112- . expect ( "Failed to list IP Pools" )
113- . all_items ;
99+ let list = get_ip_pools ( client) . await ;
114100 assert_eq ! ( list. len( ) , 1 , "Expected exactly 1 IP pool" ) ;
115101 assert_pools_eq ( & created_pool, & list[ 0 ] ) ;
116102
@@ -212,6 +198,71 @@ async fn test_ip_pool_basic_crud(cptestctx: &ControlPlaneTestContext) {
212198 . expect ( "Expected to be able to delete an empty IP Pool" ) ;
213199}
214200
201+ async fn get_ip_pools ( client : & ClientTestContext ) -> Vec < IpPool > {
202+ NexusRequest :: iter_collection_authn :: < IpPool > (
203+ client,
204+ "/v1/system/ip-pools" ,
205+ "" ,
206+ None ,
207+ )
208+ . await
209+ . expect ( "Failed to list IP Pools" )
210+ . all_items
211+ }
212+
213+ // this test exists primarily because of a bug in the initial implementation
214+ // where we included a duplicate of each pool in the list response for every
215+ // associated silo
216+ #[ nexus_test]
217+ async fn test_ip_pool_list_dedupe ( cptestctx : & ControlPlaneTestContext ) {
218+ let client = & cptestctx. external_client ;
219+
220+ let ip_pools = get_ip_pools ( & client) . await ;
221+ assert_eq ! ( ip_pools. len( ) , 0 ) ;
222+
223+ let range1 = IpRange :: V4 (
224+ Ipv4Range :: new (
225+ std:: net:: Ipv4Addr :: new ( 10 , 0 , 0 , 51 ) ,
226+ std:: net:: Ipv4Addr :: new ( 10 , 0 , 0 , 52 ) ,
227+ )
228+ . unwrap ( ) ,
229+ ) ;
230+ let ( pool1, ..) = create_ip_pool ( client, "pool1" , Some ( range1) ) . await ;
231+ let range2 = IpRange :: V4 (
232+ Ipv4Range :: new (
233+ std:: net:: Ipv4Addr :: new ( 10 , 0 , 0 , 53 ) ,
234+ std:: net:: Ipv4Addr :: new ( 10 , 0 , 0 , 54 ) ,
235+ )
236+ . unwrap ( ) ,
237+ ) ;
238+ let ( pool2, ..) = create_ip_pool ( client, "pool2" , Some ( range2) ) . await ;
239+
240+ let ip_pools = get_ip_pools ( & client) . await ;
241+ assert_eq ! ( ip_pools. len( ) , 2 ) ;
242+ assert_eq ! ( ip_pools[ 0 ] . identity. id, pool1. id( ) ) ;
243+ assert_eq ! ( ip_pools[ 1 ] . identity. id, pool2. id( ) ) ;
244+
245+ // create 3 silos and link
246+ let silo1 =
247+ create_silo ( & client, "silo1" , true , SiloIdentityMode :: SamlJit ) . await ;
248+ link_ip_pool ( client, "pool1" , & silo1. id ( ) , false ) . await ;
249+ // linking pool2 here only, just for variety
250+ link_ip_pool ( client, "pool2" , & silo1. id ( ) , false ) . await ;
251+
252+ let silo2 =
253+ create_silo ( & client, "silo2" , true , SiloIdentityMode :: SamlJit ) . await ;
254+ link_ip_pool ( client, "pool1" , & silo2. id ( ) , true ) . await ;
255+
256+ let silo3 =
257+ create_silo ( & client, "silo3" , true , SiloIdentityMode :: SamlJit ) . await ;
258+ link_ip_pool ( client, "pool1" , & silo3. id ( ) , true ) . await ;
259+
260+ let ip_pools = get_ip_pools ( & client) . await ;
261+ assert_eq ! ( ip_pools. len( ) , 2 ) ;
262+ assert_eq ! ( ip_pools[ 0 ] . identity. id, pool1. id( ) ) ;
263+ assert_eq ! ( ip_pools[ 1 ] . identity. id, pool2. id( ) ) ;
264+ }
265+
215266/// The internal IP pool, defined by its association with the internal silo,
216267/// cannot be interacted with through the operator API. CRUD operations should
217268/// all 404 except fetch by name or ID.
0 commit comments