@@ -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 {
@@ -278,4 +295,110 @@ mod tests {
278295 output_str, "Signature: 3QnHBSdd4doEF6FgpLCejqEw42UQjfvNhQJwoYDSpoBszpCCqVft4cGoneDCnZ6Ez3ujzavzUu85u6F79WtLhcsv\n "
279296 ) ;
280297 }
298+
299+ #[ test]
300+ fn test_cli_device_create_fails_when_public_ip_conflicts_with_existing_device_dz_prefix ( ) {
301+ use doublezero_sdk:: { Device , DeviceStatus } ;
302+
303+ let mut client = create_test_client ( ) ;
304+
305+ let location_pk = Pubkey :: from_str_const ( "HQ2UUt18uJqKaQFJhgV9zaTdQxUZjNrsKFgoEDquBkcx" ) ;
306+ let exchange_pk = Pubkey :: from_str_const ( "HQ2UUt18uJqKaQFJhgV9zaTdQxUZjNrsKFgoEDquBkcc" ) ;
307+ let contributor_pk = Pubkey :: from_str_const ( "HQ3UUt18uJqKaQFJhgV9zaTdQxUZjNrsKFgoEDquBkcx" ) ;
308+
309+ // Create an existing device with dz_prefix that will conflict
310+ let existing_device_pk =
311+ Pubkey :: from_str_const ( "HQ4UUt18uJqKaQFJhgV9zaTdQxUZjNrsKFgoEDquBkcx" ) ;
312+ let existing_device = Device {
313+ account_type : AccountType :: Device ,
314+ index : 1 ,
315+ bump_seed : 255 ,
316+ reference_count : 0 ,
317+ code : "existing-device" . to_string ( ) ,
318+ contributor_pk,
319+ location_pk,
320+ exchange_pk,
321+ device_type : DeviceType :: Switch ,
322+ public_ip : [ 100 , 0 , 0 , 1 ] . into ( ) ,
323+ // This dz_prefix includes 10.1.5.10
324+ dz_prefixes : "10.1.0.0/16" . parse ( ) . unwrap ( ) ,
325+ metrics_publisher_pk : Pubkey :: default ( ) ,
326+ status : DeviceStatus :: Activated ,
327+ mgmt_vrf : String :: default ( ) ,
328+ interfaces : vec ! [ ] ,
329+ users_count : 0 ,
330+ max_users : 100 ,
331+ owner : Pubkey :: default ( ) ,
332+ } ;
333+
334+ let mut devices = HashMap :: new ( ) ;
335+ devices. insert ( existing_device_pk, existing_device) ;
336+
337+ client
338+ . expect_check_requirements ( )
339+ . with ( predicate:: eq ( CHECK_ID_JSON | CHECK_BALANCE ) )
340+ . returning ( |_| Ok ( ( ) ) ) ;
341+ client
342+ . expect_list_device ( )
343+ . with ( predicate:: eq ( ListDeviceCommand ) )
344+ . returning ( move |_| Ok ( devices. clone ( ) ) ) ;
345+
346+ let mut output = Vec :: new ( ) ;
347+ // Create a device with public_ip 10.1.5.10, which is within existing device's dz_prefix
348+ let res = CreateDeviceCliCommand {
349+ code : "new-device" . to_string ( ) ,
350+ contributor : contributor_pk. to_string ( ) ,
351+ location : location_pk. to_string ( ) ,
352+ exchange : exchange_pk. to_string ( ) ,
353+ public_ip : [ 10 , 1 , 5 , 10 ] . into ( ) , // This is within 10.1.0.0/16
354+ dz_prefixes : "192.168.0.0/16" . parse ( ) . unwrap ( ) ,
355+ metrics_publisher : Some ( Pubkey :: default ( ) . to_string ( ) ) ,
356+ mgmt_vrf : String :: default ( ) ,
357+ wait : false ,
358+ }
359+ . execute ( & client, & mut output) ;
360+
361+ assert ! ( res. is_err( ) ) ;
362+ let err = res. unwrap_err ( ) ;
363+ assert ! ( err. to_string( ) . contains( "Public IP '10.1.5.10' conflicts with existing device 'existing-device' dz_prefix '10.1.0.0/16'" ) ) ;
364+ }
365+
366+ #[ test]
367+ fn test_cli_device_create_fails_when_public_ip_conflicts_with_own_dz_prefix ( ) {
368+ let mut client = create_test_client ( ) ;
369+
370+ let location_pk = Pubkey :: from_str_const ( "HQ2UUt18uJqKaQFJhgV9zaTdQxUZjNrsKFgoEDquBkcx" ) ;
371+ let exchange_pk = Pubkey :: from_str_const ( "HQ2UUt18uJqKaQFJhgV9zaTdQxUZjNrsKFgoEDquBkcc" ) ;
372+ let contributor_pk = Pubkey :: from_str_const ( "HQ3UUt18uJqKaQFJhgV9zaTdQxUZjNrsKFgoEDquBkcx" ) ;
373+
374+ client
375+ . expect_check_requirements ( )
376+ . with ( predicate:: eq ( CHECK_ID_JSON | CHECK_BALANCE ) )
377+ . returning ( |_| Ok ( ( ) ) ) ;
378+ client
379+ . expect_list_device ( )
380+ . with ( predicate:: eq ( ListDeviceCommand ) )
381+ . returning ( move |_| Ok ( HashMap :: new ( ) ) ) ;
382+
383+ let mut output = Vec :: new ( ) ;
384+ // Create a device where public_ip is within its own dz_prefix
385+ let res = CreateDeviceCliCommand {
386+ code : "test-device" . to_string ( ) ,
387+ contributor : contributor_pk. to_string ( ) ,
388+ location : location_pk. to_string ( ) ,
389+ exchange : exchange_pk. to_string ( ) ,
390+ public_ip : [ 10 , 1 , 5 , 10 ] . into ( ) , // This is within 10.1.0.0/16
391+ dz_prefixes : "10.1.0.0/16" . parse ( ) . unwrap ( ) , // Own prefix contains public_ip
392+ metrics_publisher : Some ( Pubkey :: default ( ) . to_string ( ) ) ,
393+ mgmt_vrf : String :: default ( ) ,
394+ wait : false ,
395+ }
396+ . execute ( & client, & mut output) ;
397+
398+ assert ! ( res. is_err( ) ) ;
399+ let err = res. unwrap_err ( ) ;
400+ assert ! ( err
401+ . to_string( )
402+ . contains( "Public IP '10.1.5.10' conflicts with device's own dz_prefix '10.1.0.0/16'" ) ) ;
403+ }
281404}
0 commit comments