6
6
7
7
use std:: {
8
8
borrow:: Cow ,
9
- num:: NonZeroUsize ,
9
+ num:: { NonZeroU16 , NonZeroUsize } ,
10
10
path:: { Path , PathBuf } ,
11
11
time:: Duration ,
12
12
} ;
13
13
14
- use anyhow:: bail;
14
+ use anyhow:: { bail, Context } ;
15
15
use async_trait:: async_trait;
16
16
use futures:: { stream:: FuturesUnordered , FutureExt as _, StreamExt , TryFutureExt as _} ;
17
17
use linera_base:: crypto:: { CryptoRng , KeyPair } ;
@@ -395,6 +395,40 @@ enum ServerCommand {
395
395
#[ arg( long, default_value = "1000" ) ]
396
396
cache_size : usize ,
397
397
} ,
398
+
399
+ /// Replaces the configurations of the shards by following the given template.
400
+ #[ command( name = "edit-shards" ) ]
401
+ EditShards {
402
+ /// Path to the file containing the server configuration of this Linera validator.
403
+ #[ arg( long = "server" ) ]
404
+ server_config_path : PathBuf ,
405
+
406
+ /// The number N of shard configs to generate, possibly starting with zeroes. If
407
+ /// `N` was written using `D` digits, we will replace the first occurrence of the
408
+ /// string `"%" * D` (`%` repeated D times) by the shard number.
409
+ #[ arg( long) ]
410
+ num_shards : String ,
411
+
412
+ /// The host of the validator (IP address or hostname), possibly containing `%`
413
+ /// for digits of the shard number.
414
+ #[ arg( long) ]
415
+ host : String ,
416
+
417
+ /// The port of the main endpoint, possibly containing `%` for digits of the shard
418
+ /// number.
419
+ #[ arg( long) ]
420
+ port : String ,
421
+
422
+ /// The host for the metrics endpoint, possibly containing `%` for digits of the
423
+ /// shard number.
424
+ #[ arg( long) ]
425
+ metrics_host : String ,
426
+
427
+ /// The port for the metrics endpoint, possibly containing `%` for digits of the
428
+ /// shard number.
429
+ #[ arg( long) ]
430
+ metrics_port : Option < String > ,
431
+ } ,
398
432
}
399
433
400
434
fn main ( ) {
@@ -430,7 +464,7 @@ fn log_file_name_for(command: &ServerCommand) -> Cow<'static, str> {
430
464
..
431
465
} => {
432
466
let server_config: ValidatorServerConfig =
433
- util:: read_json ( server_config_path) . expect ( "Fail to read server config" ) ;
467
+ util:: read_json ( server_config_path) . expect ( "Failed to read server config" ) ;
434
468
let name = & server_config. validator . name ;
435
469
436
470
if let Some ( shard) = shard {
@@ -440,13 +474,13 @@ fn log_file_name_for(command: &ServerCommand) -> Cow<'static, str> {
440
474
}
441
475
. into ( )
442
476
}
443
- ServerCommand :: Generate { .. } | ServerCommand :: Initialize { .. } => "server" . into ( ) ,
477
+ ServerCommand :: Generate { .. }
478
+ | ServerCommand :: Initialize { .. }
479
+ | ServerCommand :: EditShards { .. } => "server" . into ( ) ,
444
480
}
445
481
}
446
482
447
483
async fn run ( options : ServerOptions ) {
448
- linera_version:: VERSION_INFO . log ( ) ;
449
-
450
484
match options. command {
451
485
ServerCommand :: Run {
452
486
server_config_path,
@@ -461,10 +495,12 @@ async fn run(options: ServerOptions) {
461
495
max_stream_queries,
462
496
cache_size,
463
497
} => {
498
+ linera_version:: VERSION_INFO . log ( ) ;
499
+
464
500
let genesis_config: GenesisConfig =
465
- util:: read_json ( & genesis_config_path) . expect ( "Fail to read initial chain config" ) ;
501
+ util:: read_json ( & genesis_config_path) . expect ( "Failed to read initial chain config" ) ;
466
502
let server_config: ValidatorServerConfig =
467
- util:: read_json ( & server_config_path) . expect ( "Fail to read server config" ) ;
503
+ util:: read_json ( & server_config_path) . expect ( "Failed to read server config" ) ;
468
504
469
505
#[ cfg( feature = "rocksdb" ) ]
470
506
if server_config. internal_network . shards . len ( ) > 1
@@ -521,17 +557,16 @@ async fn run(options: ServerOptions) {
521
557
config_validators. push ( Persist :: into_value ( server) . validator ) ;
522
558
}
523
559
if let Some ( committee) = committee {
524
- Persist :: persist (
525
- & mut persistent:: File :: new (
526
- & committee,
527
- CommitteeConfig {
528
- validators : config_validators,
529
- } ,
530
- )
531
- . expect ( "Unable to open committee configuration" ) ,
560
+ let mut config = persistent:: File :: new (
561
+ & committee,
562
+ CommitteeConfig {
563
+ validators : config_validators,
564
+ } ,
532
565
)
533
- . await
534
- . expect ( "Unable to write committee description" ) ;
566
+ . expect ( "Unable to open committee configuration" ) ;
567
+ Persist :: persist ( & mut config)
568
+ . await
569
+ . expect ( "Unable to write committee description" ) ;
535
570
info ! ( "Wrote committee config {}" , committee. to_str( ) . unwrap( ) ) ;
536
571
}
537
572
}
@@ -544,7 +579,7 @@ async fn run(options: ServerOptions) {
544
579
cache_size,
545
580
} => {
546
581
let genesis_config: GenesisConfig =
547
- util:: read_json ( & genesis_config_path) . expect ( "Fail to read initial chain config" ) ;
582
+ util:: read_json ( & genesis_config_path) . expect ( "Failed to read initial chain config" ) ;
548
583
let common_config = CommonStoreConfig {
549
584
max_concurrent_queries,
550
585
max_stream_queries,
@@ -558,9 +593,69 @@ async fn run(options: ServerOptions) {
558
593
. await
559
594
. unwrap ( ) ;
560
595
}
596
+
597
+ ServerCommand :: EditShards {
598
+ server_config_path,
599
+ num_shards,
600
+ host,
601
+ port,
602
+ metrics_host,
603
+ metrics_port,
604
+ } => {
605
+ let mut server_config =
606
+ persistent:: File :: < ValidatorServerConfig > :: read ( & server_config_path)
607
+ . expect ( "Failed to read server config" ) ;
608
+ let shards = generate_shard_configs ( num_shards, host, port, metrics_host, metrics_port)
609
+ . expect ( "Failed to generate shard configs" ) ;
610
+ server_config. internal_network . shards = shards;
611
+ Persist :: persist ( & mut server_config)
612
+ . await
613
+ . expect ( "Failed to write updated server config" ) ;
614
+ }
561
615
}
562
616
}
563
617
618
+ fn generate_shard_configs (
619
+ num_shards : String ,
620
+ host : String ,
621
+ port : String ,
622
+ metrics_host : String ,
623
+ metrics_port : Option < String > ,
624
+ ) -> anyhow:: Result < Vec < ShardConfig > > {
625
+ let mut shards = Vec :: new ( ) ;
626
+ let len = num_shards. len ( ) ;
627
+ let num_shards = num_shards
628
+ . parse :: < NonZeroU16 > ( )
629
+ . context ( "Failed to parse the number of shards" ) ?;
630
+ let pattern = "%" . repeat ( len) ;
631
+
632
+ for i in 1u16 ..=num_shards. into ( ) {
633
+ let index = format ! ( "{i:0len$}" ) ;
634
+ let host = host. replacen ( & pattern, & index, 1 ) ;
635
+ let port = port
636
+ . replacen ( & pattern, & index, 1 )
637
+ . parse ( )
638
+ . context ( "Failed to decode port into an integers" ) ?;
639
+ let metrics_host = metrics_host. replacen ( & pattern, & index, 1 ) ;
640
+ let metrics_port = metrics_port
641
+ . as_ref ( )
642
+ . map ( |port| {
643
+ port. replacen ( & pattern, & index, 1 )
644
+ . parse ( )
645
+ . context ( "Failed to decode metrics port into an integers" )
646
+ } )
647
+ . transpose ( ) ?;
648
+ let shard = ShardConfig {
649
+ host,
650
+ port,
651
+ metrics_host,
652
+ metrics_port,
653
+ } ;
654
+ shards. push ( shard) ;
655
+ }
656
+ Ok ( shards)
657
+ }
658
+
564
659
#[ cfg( test) ]
565
660
mod test {
566
661
use linera_rpc:: simple:: TransportProtocol ;
@@ -622,4 +717,41 @@ mod test {
622
717
}
623
718
) ;
624
719
}
720
+
721
+ #[ test]
722
+ fn test_generate_shard_configs ( ) {
723
+ assert_eq ! (
724
+ generate_shard_configs(
725
+ "02" . into( ) ,
726
+ "host%%" . into( ) ,
727
+ "10%%" . into( ) ,
728
+ "metrics_host%%" . into( ) ,
729
+ Some ( "11%%" . into( ) )
730
+ )
731
+ . unwrap( ) ,
732
+ vec![
733
+ ShardConfig {
734
+ host: "host01" . into( ) ,
735
+ port: 1001 ,
736
+ metrics_host: "metrics_host01" . into( ) ,
737
+ metrics_port: Some ( 1101 ) ,
738
+ } ,
739
+ ShardConfig {
740
+ host: "host02" . into( ) ,
741
+ port: 1002 ,
742
+ metrics_host: "metrics_host02" . into( ) ,
743
+ metrics_port: Some ( 1102 ) ,
744
+ } ,
745
+ ] ,
746
+ ) ;
747
+
748
+ assert ! ( generate_shard_configs(
749
+ "2" . into( ) ,
750
+ "host%%" . into( ) ,
751
+ "10%%" . into( ) ,
752
+ "metrics_host%%" . into( ) ,
753
+ Some ( "11%%" . into( ) )
754
+ )
755
+ . is_err( ) ) ;
756
+ }
625
757
}
0 commit comments