This is an automated email from the ASF dual-hosted git repository.

hgruszecki pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iggy.git


The following commit(s) were added to refs/heads/master by this push:
     new 4107de263 feat(bench): add --username/--password and reuse admin 
client (#2778)
4107de263 is described below

commit 4107de263aafc5f19d2b5debbc8a9e68c7483115
Author: Hubert Gruszecki <[email protected]>
AuthorDate: Thu Feb 19 10:18:07 2026 +0100

    feat(bench): add --username/--password and reuse admin client (#2778)
    
    Authentication was hardcoded to default root credentials and
    post-benchmark operations created throwaway clients. Propagate
    -u/-p credentials through ClientFactory, reuse a single admin
    client in the runner. Reassign -p from --messages-per-batch
    (now -P) to --password for iggy CLI compatibility.
---
 .../bench/src/actors/consumer/client/high_level.rs |  9 ++-
 core/bench/src/actors/consumer/client/low_level.rs |  9 ++-
 .../bench/src/actors/producer/client/high_level.rs |  9 ++-
 core/bench/src/actors/producer/client/low_level.rs |  9 ++-
 core/bench/src/analytics/report_builder.rs         |  8 +--
 core/bench/src/args/common.rs                      | 23 +++++++-
 core/bench/src/args/examples.rs                    | 11 +++-
 core/bench/src/benchmarks/benchmark.rs             | 16 +++++-
 core/bench/src/benchmarks/common.rs                |  9 ++-
 core/bench/src/runner.rs                           | 21 ++++---
 core/bench/src/utils/client_factory.rs             | 65 +++++++++++++++++++---
 core/bench/src/utils/mod.rs                        | 24 ++------
 12 files changed, 157 insertions(+), 56 deletions(-)

diff --git a/core/bench/src/actors/consumer/client/high_level.rs 
b/core/bench/src/actors/consumer/client/high_level.rs
index 22b04679f..d2a63425a 100644
--- a/core/bench/src/actors/consumer/client/high_level.rs
+++ b/core/bench/src/actors/consumer/client/high_level.rs
@@ -24,7 +24,7 @@ use crate::actors::{
     },
 };
 
-use crate::utils::{ClientFactory, login_root};
+use crate::utils::{ClientFactory, authenticate};
 use futures_util::StreamExt;
 use iggy::prelude::*;
 use std::{sync::Arc, time::Duration};
@@ -104,7 +104,12 @@ impl BenchmarkInit for HighLevelConsumerClient {
         let topic_id_str = "topic-1";
         let client = self.client_factory.create_client().await;
         let client = IggyClient::create(client, None, None);
-        login_root(&client).await;
+        authenticate(
+            &client,
+            self.client_factory.username(),
+            self.client_factory.password(),
+        )
+        .await;
 
         let stream_id_str = self.config.stream_id.clone();
 
diff --git a/core/bench/src/actors/consumer/client/low_level.rs 
b/core/bench/src/actors/consumer/client/low_level.rs
index b501942a0..0e4bf1358 100644
--- a/core/bench/src/actors/consumer/client/low_level.rs
+++ b/core/bench/src/actors/consumer/client/low_level.rs
@@ -20,7 +20,7 @@ use crate::actors::consumer::client::BenchmarkConsumerClient;
 use crate::actors::consumer::client::interface::{BenchmarkConsumerConfig, 
ConsumerClient};
 use crate::actors::{ApiLabel, BatchMetrics, BenchmarkInit};
 use crate::benchmarks::common::create_consumer;
-use crate::utils::{ClientFactory, login_root};
+use crate::utils::{ClientFactory, authenticate};
 use crate::utils::{batch_total_size_bytes, batch_user_size_bytes};
 use iggy::prelude::*;
 use std::sync::Arc;
@@ -121,7 +121,12 @@ impl BenchmarkInit for LowLevelConsumerClient {
 
         let client = self.client_factory.create_client().await;
         let client = IggyClient::create(client, None, None);
-        login_root(&client).await;
+        authenticate(
+            &client,
+            self.client_factory.username(),
+            self.client_factory.password(),
+        )
+        .await;
 
         let stream_id: Identifier = 
self.config.stream_id.as_str().try_into().unwrap();
         let topic_id = topic_id_str.try_into().unwrap();
diff --git a/core/bench/src/actors/producer/client/high_level.rs 
b/core/bench/src/actors/producer/client/high_level.rs
index 7fdf923b1..c532518f1 100644
--- a/core/bench/src/actors/producer/client/high_level.rs
+++ b/core/bench/src/actors/producer/client/high_level.rs
@@ -16,7 +16,7 @@
  * under the License.
  */
 
-use crate::utils::{ClientFactory, login_root};
+use crate::utils::{ClientFactory, authenticate};
 use crate::{
     actors::{
         ApiLabel, BatchMetrics, BenchmarkInit,
@@ -84,7 +84,12 @@ impl BenchmarkInit for HighLevelProducerClient {
 
         let client = self.client_factory.create_client().await;
         let client = IggyClient::create(client, None, None);
-        login_root(&client).await;
+        authenticate(
+            &client,
+            self.client_factory.username(),
+            self.client_factory.password(),
+        )
+        .await;
 
         let stream_id_str = self.config.stream_id.clone();
 
diff --git a/core/bench/src/actors/producer/client/low_level.rs 
b/core/bench/src/actors/producer/client/low_level.rs
index b132f9a97..d2f9e07f5 100644
--- a/core/bench/src/actors/producer/client/low_level.rs
+++ b/core/bench/src/actors/producer/client/low_level.rs
@@ -18,7 +18,7 @@
 
 use std::sync::Arc;
 
-use crate::utils::{ClientFactory, login_root};
+use crate::utils::{ClientFactory, authenticate};
 use crate::{
     actors::{
         ApiLabel, BatchMetrics, BenchmarkInit,
@@ -92,7 +92,12 @@ impl BenchmarkInit for LowLevelProducerClient {
 
         let client = self.client_factory.create_client().await;
         let client = IggyClient::create(client, None, None);
-        login_root(&client).await;
+        authenticate(
+            &client,
+            self.client_factory.username(),
+            self.client_factory.password(),
+        )
+        .await;
 
         let partitioning = match partitions {
             0 => panic!("Partition count must be greater than 0"),
diff --git a/core/bench/src/analytics/report_builder.rs 
b/core/bench/src/analytics/report_builder.rs
index 2ec001091..48383f95d 100644
--- a/core/bench/src/analytics/report_builder.rs
+++ b/core/bench/src/analytics/report_builder.rs
@@ -19,7 +19,6 @@
 use std::{collections::HashMap, thread};
 
 use super::metrics::group::{from_individual_metrics, 
from_producers_and_consumers_statistics};
-use crate::utils::ClientFactory;
 use crate::utils::get_server_stats;
 use bench_report::{
     actor_kind::ActorKind,
@@ -31,8 +30,7 @@ use bench_report::{
     server_stats::{BenchmarkCacheMetrics, BenchmarkCacheMetricsKey, 
BenchmarkServerStats},
 };
 use chrono::{DateTime, Utc};
-use iggy::prelude::{CacheMetrics, CacheMetricsKey, IggyTimestamp, Stats};
-use std::sync::Arc;
+use iggy::prelude::{CacheMetrics, CacheMetricsKey, IggyClient, IggyTimestamp, 
Stats};
 
 pub struct BenchmarkReportBuilder;
 
@@ -43,7 +41,7 @@ impl BenchmarkReportBuilder {
         mut params: BenchmarkParams,
         mut individual_metrics: Vec<BenchmarkIndividualMetrics>,
         moving_average_window: u32,
-        client_factory: &Arc<dyn ClientFactory>,
+        admin_client: &IggyClient,
     ) -> BenchmarkReport {
         let uuid = uuid::Uuid::new_v4();
 
@@ -51,7 +49,7 @@ impl BenchmarkReportBuilder {
             
DateTime::<Utc>::from_timestamp_micros(IggyTimestamp::now().as_micros() as i64)
                 .map_or_else(|| String::from("unknown"), |dt| dt.to_rfc3339());
 
-        let server_stats = get_server_stats(client_factory)
+        let server_stats = get_server_stats(admin_client)
             .await
             .expect("Failed to get server stats");
 
diff --git a/core/bench/src/args/common.rs b/core/bench/src/args/common.rs
index be983c209..8c1c535a5 100644
--- a/core/bench/src/args/common.rs
+++ b/core/bench/src/args/common.rs
@@ -30,7 +30,10 @@ use bench_report::benchmark_kind::BenchmarkKind;
 use bench_report::numeric_parameter::BenchmarkNumericParameter;
 use clap::error::ErrorKind;
 use clap::{CommandFactory, Parser};
-use iggy::prelude::{IggyByteSize, IggyDuration, IggyExpiry, TransportProtocol};
+use iggy::prelude::{
+    DEFAULT_ROOT_PASSWORD, DEFAULT_ROOT_USERNAME, IggyByteSize, IggyDuration, 
IggyExpiry,
+    TransportProtocol,
+};
 use std::num::NonZeroU32;
 use std::str::FromStr;
 
@@ -47,7 +50,7 @@ pub struct IggyBenchArgs {
     pub message_size: BenchmarkNumericParameter,
 
     /// Number of messages per batch
-    #[arg(long, short = 'p', value_parser = 
BenchmarkNumericParameter::from_str, default_value_t = 
BenchmarkNumericParameter::Value(DEFAULT_MESSAGES_PER_BATCH.get()))]
+    #[arg(long, short = 'P', value_parser = 
BenchmarkNumericParameter::from_str, default_value_t = 
BenchmarkNumericParameter::Value(DEFAULT_MESSAGES_PER_BATCH.get()))]
     pub messages_per_batch: BenchmarkNumericParameter,
 
     /// Number of message batches per actor (producer / consumer / producing 
consumer).
@@ -80,6 +83,14 @@ pub struct IggyBenchArgs {
     /// Use high-level API for actors
     #[arg(long, short = 'H', default_value_t = false)]
     pub high_level_api: bool,
+
+    /// Username for server authentication
+    #[arg(long, short = 'u', default_value_t = 
DEFAULT_ROOT_USERNAME.to_string())]
+    pub username: String,
+
+    /// Password for server authentication
+    #[arg(long, short = 'p', default_value_t = 
DEFAULT_ROOT_PASSWORD.to_string())]
+    pub password: String,
 }
 
 impl IggyBenchArgs {
@@ -303,6 +314,14 @@ impl IggyBenchArgs {
         self.high_level_api
     }
 
+    pub fn username(&self) -> &str {
+        &self.username
+    }
+
+    pub fn password(&self) -> &str {
+        &self.password
+    }
+
     /// Generates the output directory name based on benchmark parameters.
     pub fn generate_dir_name(&self) -> String {
         let benchmark_kind = match &self.benchmark_kind {
diff --git a/core/bench/src/args/examples.rs b/core/bench/src/args/examples.rs
index 95cb9f9d9..40c63ced3 100644
--- a/core/bench/src/args/examples.rs
+++ b/core/bench/src/args/examples.rs
@@ -50,7 +50,7 @@ const EXAMPLES: &str = r#"EXAMPLES:
     You can customize various parameters for any benchmark mode:
 
     Global options (before the benchmark command):
-    --messages-per-batch (-p): Number of messages per batch [default: 1000]
+    --messages-per-batch (-P): Number of messages per batch [default: 1000]
                                For random batch sizes, use range format: 
"100..1000"
     --message-batches (-b): Total number of batches [default: 1000]
     --total-messages-size (-T): Total size of messages to send (e.g., "1GB", 
"500MB")
@@ -61,6 +61,8 @@ const EXAMPLES: &str = r#"EXAMPLES:
     --warmup-time (-w): Warmup duration [default: 0s]
     --sampling-time (-t): Metrics sampling interval [default: 10ms]
     --moving-average-window (-W): Window size for moving average [default: 20]
+    --username (-u): Username for server authentication [default: iggy]
+    --password (-p): Password for server authentication [default: iggy]
 
     Benchmark-specific options (after the benchmark command):
     --streams (-s): Number of streams
@@ -122,6 +124,13 @@ const EXAMPLES: &str = r#"EXAMPLES:
         --streams 5 --producers 5 \
         tcp --server-address 192.168.1.100:8090
 
+    With custom credentials:
+
+    $ cargo r -r --bin iggy-bench -- \
+        --username admin --password secret \
+        pinned-producer --streams 5 --producers 5 \
+        tcp --server-address 192.168.1.100:8090
+
 6) Output Data and Results:
 
     The benchmark tool can store detailed results for analysis and comparison:
diff --git a/core/bench/src/benchmarks/benchmark.rs 
b/core/bench/src/benchmarks/benchmark.rs
index 594a00101..7a00c6528 100644
--- a/core/bench/src/benchmarks/benchmark.rs
+++ b/core/bench/src/benchmarks/benchmark.rs
@@ -17,7 +17,7 @@
  */
 
 use crate::args::kind::BenchmarkKindCommand;
-use crate::utils::{ClientFactory, login_root};
+use crate::utils::{ClientFactory, authenticate};
 use crate::{args::common::IggyBenchArgs, 
utils::client_factory::create_client_factory};
 use async_trait::async_trait;
 use bench_report::benchmark_kind::BenchmarkKind;
@@ -99,7 +99,12 @@ pub trait Benchmarkable: Send {
         let partitions_count: u32 = self.args().number_of_partitions();
         let client = self.client_factory().create_client().await;
         let client = IggyClient::create(client, None, None);
-        login_root(&client).await;
+        authenticate(
+            &client,
+            self.client_factory().username(),
+            self.client_factory().password(),
+        )
+        .await;
         let streams = client.get_streams().await?;
         for i in 1..=number_of_streams {
             let stream_name = format!("bench-stream-{i}");
@@ -139,7 +144,12 @@ pub trait Benchmarkable: Send {
         let number_of_streams = self.args().streams();
         let client = self.client_factory().create_client().await;
         let client = IggyClient::create(client, None, None);
-        login_root(&client).await;
+        authenticate(
+            &client,
+            self.client_factory().username(),
+            self.client_factory().password(),
+        )
+        .await;
         let streams = client.get_streams().await?;
         for i in 1..=number_of_streams {
             let stream_name = format!("bench-stream-{i}");
diff --git a/core/bench/src/benchmarks/common.rs 
b/core/bench/src/benchmarks/common.rs
index 7858f6fca..69a20cc62 100644
--- a/core/bench/src/benchmarks/common.rs
+++ b/core/bench/src/benchmarks/common.rs
@@ -17,7 +17,7 @@
  */
 
 use super::{CONSUMER_GROUP_BASE_ID, CONSUMER_GROUP_NAME_PREFIX};
-use crate::utils::{ClientFactory, login_root};
+use crate::utils::{ClientFactory, authenticate};
 use crate::{
     actors::{
         consumer::typed_benchmark_consumer::TypedBenchmarkConsumer,
@@ -76,7 +76,12 @@ pub async fn init_consumer_groups(
     let client = IggyClient::create(client, None, None);
     let cg_count = args.number_of_consumer_groups();
 
-    login_root(&client).await;
+    authenticate(
+        &client,
+        client_factory.username(),
+        client_factory.password(),
+    )
+    .await;
     for i in 1..=cg_count {
         let consumer_group_id = CONSUMER_GROUP_BASE_ID + i;
         let stream_name = format!("bench-stream-{i}");
diff --git a/core/bench/src/runner.rs b/core/bench/src/runner.rs
index d9007bfd7..40daf2bf1 100644
--- a/core/bench/src/runner.rs
+++ b/core/bench/src/runner.rs
@@ -23,7 +23,7 @@ use crate::plot::{ChartType, plot_chart};
 use crate::utils::cpu_name::append_cpu_name_lowercase;
 use crate::utils::{collect_server_logs_and_save_to_file, 
params_from_args_and_metrics};
 use bench_report::hardware::BenchmarkHardware;
-use iggy::prelude::IggyError;
+use iggy::prelude::{Client, IggyClient, IggyError, UserClient};
 use std::path::Path;
 use std::time::Duration;
 use tokio::time::sleep;
@@ -62,6 +62,15 @@ impl BenchmarkRunner {
         }
 
         info!("All actors joined!");
+
+        let client_factory = benchmark.client_factory();
+        let admin_client_wrapper = client_factory.create_client().await;
+        let admin_client = IggyClient::create(admin_client_wrapper, None, 
None);
+        admin_client.connect().await?;
+        admin_client
+            .login_user(client_factory.username(), client_factory.password())
+            .await?;
+
         let hardware =
             
BenchmarkHardware::get_system_info_with_identifier(benchmark.args().identifier());
         let params = params_from_args_and_metrics(benchmark.args(), 
&individual_metrics);
@@ -71,7 +80,7 @@ impl BenchmarkRunner {
             params,
             individual_metrics,
             benchmark.args().moving_average_window(),
-            benchmark.client_factory(),
+            &admin_client,
         )
         .await;
 
@@ -92,11 +101,9 @@ impl BenchmarkRunner {
             // Dump the report to JSON
             report.dump_to_json(&full_output_path);
 
-            if let Err(e) = collect_server_logs_and_save_to_file(
-                benchmark.client_factory(),
-                Path::new(&full_output_path),
-            )
-            .await
+            if let Err(e) =
+                collect_server_logs_and_save_to_file(&admin_client, 
Path::new(&full_output_path))
+                    .await
             {
                 error!("Failed to collect server logs: {e}");
             }
diff --git a/core/bench/src/utils/client_factory.rs 
b/core/bench/src/utils/client_factory.rs
index 3fda38d77..171c05240 100644
--- a/core/bench/src/utils/client_factory.rs
+++ b/core/bench/src/utils/client_factory.rs
@@ -21,9 +21,8 @@ use crate::args::transport::BenchmarkTransportCommand;
 use async_trait::async_trait;
 use iggy::http::http_client::HttpClient;
 use iggy::prelude::{
-    Client, ClientWrapper, DEFAULT_ROOT_PASSWORD, DEFAULT_ROOT_USERNAME, 
HttpClientConfig,
-    IdentityInfo, IggyClient, QuicClientConfig, TcpClient, TcpClientConfig, 
TransportProtocol,
-    UserClient, WebSocketClientConfig,
+    Client, ClientWrapper, HttpClientConfig, IdentityInfo, IggyClient, 
QuicClientConfig, TcpClient,
+    TcpClientConfig, TransportProtocol, UserClient, WebSocketClientConfig,
 };
 use iggy::quic::quic_client::QuicClient;
 use iggy::websocket::websocket_client::WebSocketClient;
@@ -34,18 +33,19 @@ pub trait ClientFactory: Sync + Send {
     async fn create_client(&self) -> ClientWrapper;
     fn transport(&self) -> TransportProtocol;
     fn server_addr(&self) -> String;
+    fn username(&self) -> &str;
+    fn password(&self) -> &str;
 }
 
-pub async fn login_root(client: &IggyClient) -> IdentityInfo {
-    client
-        .login_user(DEFAULT_ROOT_USERNAME, DEFAULT_ROOT_PASSWORD)
-        .await
-        .unwrap()
+pub async fn authenticate(client: &IggyClient, username: &str, password: &str) 
-> IdentityInfo {
+    client.login_user(username, password).await.unwrap()
 }
 
 #[derive(Debug, Clone)]
 pub struct HttpClientFactory {
     pub server_addr: String,
+    pub username: String,
+    pub password: String,
 }
 
 #[async_trait]
@@ -66,6 +66,14 @@ impl ClientFactory for HttpClientFactory {
     fn server_addr(&self) -> String {
         self.server_addr.clone()
     }
+
+    fn username(&self) -> &str {
+        &self.username
+    }
+
+    fn password(&self) -> &str {
+        &self.password
+    }
 }
 
 #[derive(Debug, Clone, Default)]
@@ -76,6 +84,8 @@ pub struct TcpClientFactory {
     pub tls_domain: String,
     pub tls_ca_file: Option<String>,
     pub tls_validate_certificate: bool,
+    pub username: String,
+    pub password: String,
 }
 
 #[async_trait]
@@ -122,11 +132,21 @@ impl ClientFactory for TcpClientFactory {
     fn server_addr(&self) -> String {
         self.server_addr.clone()
     }
+
+    fn username(&self) -> &str {
+        &self.username
+    }
+
+    fn password(&self) -> &str {
+        &self.password
+    }
 }
 
 #[derive(Debug, Clone)]
 pub struct QuicClientFactory {
     pub server_addr: String,
+    pub username: String,
+    pub password: String,
 }
 
 #[async_trait]
@@ -149,11 +169,21 @@ impl ClientFactory for QuicClientFactory {
     fn server_addr(&self) -> String {
         self.server_addr.clone()
     }
+
+    fn username(&self) -> &str {
+        &self.username
+    }
+
+    fn password(&self) -> &str {
+        &self.password
+    }
 }
 
 #[derive(Debug, Clone)]
 pub struct WebSocketClientFactory {
     pub server_addr: String,
+    pub username: String,
+    pub password: String,
 }
 
 #[async_trait]
@@ -175,12 +205,25 @@ impl ClientFactory for WebSocketClientFactory {
     fn server_addr(&self) -> String {
         self.server_addr.clone()
     }
+
+    fn username(&self) -> &str {
+        &self.username
+    }
+
+    fn password(&self) -> &str {
+        &self.password
+    }
 }
 
 pub fn create_client_factory(args: &IggyBenchArgs) -> Arc<dyn ClientFactory> {
+    let username = args.username().to_owned();
+    let password = args.password().to_owned();
+
     match &args.transport() {
         TransportProtocol::Http => Arc::new(HttpClientFactory {
             server_addr: args.server_address().to_owned(),
+            username,
+            password,
         }),
         TransportProtocol::Tcp => {
             let transport_command = args.transport_command();
@@ -192,6 +235,8 @@ pub fn create_client_factory(args: &IggyBenchArgs) -> 
Arc<dyn ClientFactory> {
                     tls_domain: tcp_args.tls_domain.clone(),
                     tls_ca_file: tcp_args.tls_ca_file.clone(),
                     tls_validate_certificate: 
tcp_args.tls_validate_certificate,
+                    username,
+                    password,
                 })
             } else {
                 unreachable!("Transport is TCP but transport command is not 
TcpArgs")
@@ -199,9 +244,13 @@ pub fn create_client_factory(args: &IggyBenchArgs) -> 
Arc<dyn ClientFactory> {
         }
         TransportProtocol::Quic => Arc::new(QuicClientFactory {
             server_addr: args.server_address().to_owned(),
+            username,
+            password,
         }),
         TransportProtocol::WebSocket => Arc::new(WebSocketClientFactory {
             server_addr: args.server_address().to_owned(),
+            username,
+            password,
         }),
     }
 }
diff --git a/core/bench/src/utils/mod.rs b/core/bench/src/utils/mod.rs
index e8ee8ebd3..82fdc9b3f 100644
--- a/core/bench/src/utils/mod.rs
+++ b/core/bench/src/utils/mod.rs
@@ -22,7 +22,7 @@ use bench_report::{
     transport::BenchmarkTransport,
 };
 use iggy::prelude::*;
-use std::{fs, path::Path, sync::Arc};
+use std::{fs, path::Path};
 use tracing::{error, info};
 
 use crate::args::{
@@ -37,7 +37,7 @@ use crate::args::{
     },
 };
 
-pub use client_factory::{ClientFactory, login_root};
+pub use client_factory::{ClientFactory, authenticate};
 
 pub mod batch_generator;
 pub mod client_factory;
@@ -61,30 +61,14 @@ pub fn batch_user_size_bytes(polled_messages: 
&PolledMessages) -> u64 {
         .sum()
 }
 
-pub async fn get_server_stats(client_factory: &Arc<dyn ClientFactory>) -> 
Result<Stats, IggyError> {
-    let client = client_factory.create_client().await;
-    let client = IggyClient::create(client, None, None);
-
-    client.connect().await?;
-    client
-        .login_user(DEFAULT_ROOT_USERNAME, DEFAULT_ROOT_PASSWORD)
-        .await?;
-
+pub async fn get_server_stats(client: &IggyClient) -> Result<Stats, IggyError> 
{
     client.get_stats().await
 }
 
 pub async fn collect_server_logs_and_save_to_file(
-    client_factory: &Arc<dyn ClientFactory>,
+    client: &IggyClient,
     output_dir: &Path,
 ) -> Result<(), IggyError> {
-    let client = client_factory.create_client().await;
-    let client = IggyClient::create(client, None, None);
-
-    client.connect().await?;
-    client
-        .login_user(DEFAULT_ROOT_USERNAME, DEFAULT_ROOT_PASSWORD)
-        .await?;
-
     let snapshot = client
         .snapshot(
             SnapshotCompression::Deflated,

Reply via email to