@@ -57,16 +57,39 @@ impl CreateDeviceCliCommand {
5757
5858 let devices = client. list_device ( ListDeviceCommand ) ?;
5959 if devices. iter ( ) . any ( |( _, d) | d. code == self . code ) {
60- return Err ( eyre:: eyre !(
60+ eyre:: bail !(
6161 "Device with code '{}' already exists" ,
6262 self . code
63- ) ) ;
63+ ) ;
6464 }
6565 if devices. iter ( ) . any ( |( _, d) | d. public_ip == self . public_ip ) {
66- return Err ( eyre:: eyre !(
66+ eyre:: bail !(
6767 "Device with public ip '{}' already exists" ,
6868 & self . public_ip
69- ) ) ;
69+ ) ;
70+ }
71+
72+ for device in devices. values ( ) {
73+ for dz_prefix in device. dz_prefixes . iter ( ) {
74+ if dz_prefix. contains ( self . public_ip ) {
75+ eyre:: bail!(
76+ "Public IP '{}' conflicts with existing device '{}' dz_prefix '{}'" ,
77+ self . public_ip,
78+ device. code,
79+ dz_prefix
80+ ) ;
81+ }
82+ }
83+ }
84+
85+ for dz_prefix in self . dz_prefixes . iter ( ) {
86+ if dz_prefix. contains ( self . public_ip ) {
87+ eyre:: bail!(
88+ "Public IP '{}' conflicts with device's own dz_prefix '{}'" ,
89+ self . public_ip,
90+ dz_prefix
91+ ) ;
92+ }
7093 }
7194
7295 let contributor_pk = match parse_pubkey ( & self . contributor ) {
@@ -111,7 +134,7 @@ impl CreateDeviceCliCommand {
111134 } else {
112135 match Pubkey :: from_str ( metrics_publisher) {
113136 Ok ( pk) => pk,
114- Err ( _) => return Err ( eyre:: eyre !( "Invalid metrics publisher Pubkey" ) ) ,
137+ Err ( _) => eyre:: bail !( "Invalid metrics publisher Pubkey" ) ,
115138 }
116139 }
117140 } else {
@@ -278,4 +301,110 @@ mod tests {
278301 output_str, "Signature: 3QnHBSdd4doEF6FgpLCejqEw42UQjfvNhQJwoYDSpoBszpCCqVft4cGoneDCnZ6Ez3ujzavzUu85u6F79WtLhcsv\n "
279302 ) ;
280303 }
304+
305+ #[ test]
306+ fn test_cli_device_create_fails_when_public_ip_conflicts_with_existing_device_dz_prefix ( ) {
307+ use doublezero_sdk:: { Device , DeviceStatus } ;
308+
309+ let mut client = create_test_client ( ) ;
310+
311+ let location_pk = Pubkey :: from_str_const ( "HQ2UUt18uJqKaQFJhgV9zaTdQxUZjNrsKFgoEDquBkcx" ) ;
312+ let exchange_pk = Pubkey :: from_str_const ( "HQ2UUt18uJqKaQFJhgV9zaTdQxUZjNrsKFgoEDquBkcc" ) ;
313+ let contributor_pk = Pubkey :: from_str_const ( "HQ3UUt18uJqKaQFJhgV9zaTdQxUZjNrsKFgoEDquBkcx" ) ;
314+
315+ // Create an existing device with dz_prefix that will conflict
316+ let existing_device_pk =
317+ Pubkey :: from_str_const ( "HQ4UUt18uJqKaQFJhgV9zaTdQxUZjNrsKFgoEDquBkcx" ) ;
318+ let existing_device = Device {
319+ account_type : AccountType :: Device ,
320+ index : 1 ,
321+ bump_seed : 255 ,
322+ reference_count : 0 ,
323+ code : "existing-device" . to_string ( ) ,
324+ contributor_pk,
325+ location_pk,
326+ exchange_pk,
327+ device_type : DeviceType :: Switch ,
328+ public_ip : [ 100 , 0 , 0 , 1 ] . into ( ) ,
329+ // This dz_prefix includes 10.1.5.10
330+ dz_prefixes : "10.1.0.0/16" . parse ( ) . unwrap ( ) ,
331+ metrics_publisher_pk : Pubkey :: default ( ) ,
332+ status : DeviceStatus :: Activated ,
333+ mgmt_vrf : String :: default ( ) ,
334+ interfaces : vec ! [ ] ,
335+ users_count : 0 ,
336+ max_users : 100 ,
337+ owner : Pubkey :: default ( ) ,
338+ } ;
339+
340+ let mut devices = HashMap :: new ( ) ;
341+ devices. insert ( existing_device_pk, existing_device) ;
342+
343+ client
344+ . expect_check_requirements ( )
345+ . with ( predicate:: eq ( CHECK_ID_JSON | CHECK_BALANCE ) )
346+ . returning ( |_| Ok ( ( ) ) ) ;
347+ client
348+ . expect_list_device ( )
349+ . with ( predicate:: eq ( ListDeviceCommand ) )
350+ . returning ( move |_| Ok ( devices. clone ( ) ) ) ;
351+
352+ let mut output = Vec :: new ( ) ;
353+ // Create a device with public_ip 10.1.5.10, which is within existing device's dz_prefix
354+ let res = CreateDeviceCliCommand {
355+ code : "new-device" . to_string ( ) ,
356+ contributor : contributor_pk. to_string ( ) ,
357+ location : location_pk. to_string ( ) ,
358+ exchange : exchange_pk. to_string ( ) ,
359+ public_ip : [ 10 , 1 , 5 , 10 ] . into ( ) , // This is within 10.1.0.0/16
360+ dz_prefixes : "192.168.0.0/16" . parse ( ) . unwrap ( ) ,
361+ metrics_publisher : Some ( Pubkey :: default ( ) . to_string ( ) ) ,
362+ mgmt_vrf : String :: default ( ) ,
363+ wait : false ,
364+ }
365+ . execute ( & client, & mut output) ;
366+
367+ assert ! ( res. is_err( ) ) ;
368+ let err = res. unwrap_err ( ) ;
369+ assert ! ( err. to_string( ) . contains( "Public IP '10.1.5.10' conflicts with existing device 'existing-device' dz_prefix '10.1.0.0/16'" ) ) ;
370+ }
371+
372+ #[ test]
373+ fn test_cli_device_create_fails_when_public_ip_conflicts_with_own_dz_prefix ( ) {
374+ let mut client = create_test_client ( ) ;
375+
376+ let location_pk = Pubkey :: from_str_const ( "HQ2UUt18uJqKaQFJhgV9zaTdQxUZjNrsKFgoEDquBkcx" ) ;
377+ let exchange_pk = Pubkey :: from_str_const ( "HQ2UUt18uJqKaQFJhgV9zaTdQxUZjNrsKFgoEDquBkcc" ) ;
378+ let contributor_pk = Pubkey :: from_str_const ( "HQ3UUt18uJqKaQFJhgV9zaTdQxUZjNrsKFgoEDquBkcx" ) ;
379+
380+ client
381+ . expect_check_requirements ( )
382+ . with ( predicate:: eq ( CHECK_ID_JSON | CHECK_BALANCE ) )
383+ . returning ( |_| Ok ( ( ) ) ) ;
384+ client
385+ . expect_list_device ( )
386+ . with ( predicate:: eq ( ListDeviceCommand ) )
387+ . returning ( move |_| Ok ( HashMap :: new ( ) ) ) ;
388+
389+ let mut output = Vec :: new ( ) ;
390+ // Create a device where public_ip is within its own dz_prefix
391+ let res = CreateDeviceCliCommand {
392+ code : "test-device" . to_string ( ) ,
393+ contributor : contributor_pk. to_string ( ) ,
394+ location : location_pk. to_string ( ) ,
395+ exchange : exchange_pk. to_string ( ) ,
396+ public_ip : [ 10 , 1 , 5 , 10 ] . into ( ) , // This is within 10.1.0.0/16
397+ dz_prefixes : "10.1.0.0/16" . parse ( ) . unwrap ( ) , // Own prefix contains public_ip
398+ metrics_publisher : Some ( Pubkey :: default ( ) . to_string ( ) ) ,
399+ mgmt_vrf : String :: default ( ) ,
400+ wait : false ,
401+ }
402+ . execute ( & client, & mut output) ;
403+
404+ assert ! ( res. is_err( ) ) ;
405+ let err = res. unwrap_err ( ) ;
406+ assert ! ( err
407+ . to_string( )
408+ . contains( "Public IP '10.1.5.10' conflicts with device's own dz_prefix '10.1.0.0/16'" ) ) ;
409+ }
281410}
0 commit comments