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