swanling/
lib.rs

1//! # Swanling
2//!
3//! Have you ever been attacked by a swanling?
4//!
5//! Swanling is a load testing framework inspired by [Locust](https://locust.io/).
6//! User behavior is defined with standard Rust code.
7//!
8//! Swanling load tests, called Swanling Attacks, are built by creating an application
9//! with Cargo, and declaring a dependency on the Swanling library.
10//!
11//! Swanling uses [`reqwest`](https://docs.rs/reqwest/) to provide a convenient HTTP
12//! client.
13//!
14//! ## Documentation
15//!
16//! - [README](https://github.com/begleybrothers/swanling/blob/main/README.md)
17//! - [Developer documentation](https://docs.rs/swanling/)
18//! ## Creating and running a Swanling load test
19//!
20//! ### Creating a simple Swanling load test
21//!
22//! First create a new empty cargo application, for example:
23//!
24//! ```bash
25//! $ cargo new loadtest
26//!      Created binary (application) `loadtest` package
27//! $ cd loadtest/
28//! ```
29//!
30//! Add Swanling as a dependency in `Cargo.toml`:
31//!
32//! ```toml
33//! [dependencies]
34//! swanling = "0.12"
35//! ```
36//!
37//! Add the following boilerplate `use` declaration at the top of your `src/main.rs`:
38//!
39//! ```rust
40//! use swanling::prelude::*;
41//! ```
42//!
43//! Using the above prelude will automatically add the following `use` statements
44//! necessary for your load test, so you don't need to manually add them:
45//!
46//! ```rust
47//! use swanling::swanling::{
48//!     SwanlingTask, SwanlingTaskError, SwanlingTaskFunction, SwanlingTaskResult, SwanlingTaskSet, SwanlingUser,
49//! };
50//! use swanling::metrics::SwanlingMetrics;
51//! use swanling::{
52//!     task, taskset, SwanlingAttack, SwanlingDefault, SwanlingDefaultType, SwanlingError, SwanlingScheduler,
53//! };
54//! ```
55//!
56//! Below your `main` function (which currently is the default `Hello, world!`), add
57//! one or more load test functions. The names of these functions are arbitrary, but it is
58//! recommended you use self-documenting names. Load test functions must be async. Each load
59//! test function must accept a reference to a [`SwanlingUser`](./swanling/struct.SwanlingUser.html) object
60//! and return a [`SwanlingTaskResult`](./swanling/type.SwanlingTaskResult.html). For example:
61//!
62//! ```rust
63//! use swanling::prelude::*;
64//!
65//! async fn loadtest_foo(user: &SwanlingUser) -> SwanlingTaskResult {
66//!   let _swanling = user.get("/path/to/foo").await?;
67//!
68//!   Ok(())
69//! }
70//! ```
71//!
72//! In the above example, we're using the [`SwanlingUser`](./swanling/struct.SwanlingUser.html) helper
73//! [`get`](./swanling/struct.SwanlingUser.html#method.get) to load a path on the website we are load
74//! testing. This helper creates a
75//! [`reqwest::RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html)
76//! object and uses it to build and execute a request for the above path. If you want access
77//! to the [`RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html)
78//! object, you can instead use the [`swanling_get`](./swanling/struct.SwanlingUser.html#method.swanling_get)
79//! helper, for example to set a timeout on this specific request:
80//!
81//! ```rust
82//! use std::time;
83//!
84//! use swanling::prelude::*;
85//!
86//! async fn loadtest_bar(user: &SwanlingUser) -> SwanlingTaskResult {
87//!     let request_builder = user.swanling_get("/path/to/bar").await?;
88//!     let _swanling = user.swanling_send(request_builder.timeout(time::Duration::from_secs(3)), None).await?;
89//!
90//!     Ok(())
91//! }
92//! ```
93//!
94//! We pass the [`RequestBuilder`](https://docs.rs/reqwest/*/reqwest/struct.RequestBuilder.html)
95//! object to [`swanling_send`](./swanling/struct.SwanlingUser.html#method.swanling_send) which builds and
96//! executes it, also collecting useful metrics. The
97//! [`.await`](https://doc.rust-lang.org/std/keyword.await.html) at the end is necessary as
98//! [`swanling_send`](./swanling/struct.SwanlingUser.html#method.swanling_send) is an async function.
99//!
100//! Once all our tasks are created, we edit the main function to initialize swanling and register
101//! the tasks. In this very simple example we only have two tasks to register, while in a real
102//! load test you can have any number of task sets with any number of individual tasks.
103//!
104//! ```rust
105//! use swanling::prelude::*;
106//!
107//! fn main() -> Result<(), SwanlingError> {
108//!     let _swanling_metrics = SwanlingAttack::initialize()?
109//!         .register_taskset(taskset!("LoadtestTasks")
110//!             // Register the foo task, assigning it a weight of 10.
111//!             .register_task(task!(loadtest_foo).set_weight(10)?)
112//!             // Register the bar task, assigning it a weight of 2 (so it
113//!             // runs 1/5 as often as bar). Apply a task name which shows up
114//!             // in metrics.
115//!             .register_task(task!(loadtest_bar).set_name("bar").set_weight(2)?)
116//!         )
117//!         // You could also set a default host here, for example:
118//!         .set_default(SwanlingDefault::Host, "http://dev.local/")?
119//!         // We set a default run time so this test runs to completion.
120//!         .set_default(SwanlingDefault::RunTime, 1)?
121//!         .execute()?;
122//!
123//!     Ok(())
124//! }
125//!
126//! // A task function that loads `/path/to/foo`.
127//! async fn loadtest_foo(user: &SwanlingUser) -> SwanlingTaskResult {
128//!     let _swanling = user.get("/path/to/foo").await?;
129//!
130//!     Ok(())
131//! }
132//!
133//! // A task function that loads `/path/to/bar`.
134//! async fn loadtest_bar(user: &SwanlingUser) -> SwanlingTaskResult {
135//!     let _swanling = user.get("/path/to/bar").await?;
136//!
137//!     Ok(())
138//! }
139//! ```
140//!
141//! Swanling now spins up a configurable number of users, each simulating a user on your
142//! website. Thanks to [`reqwest`](https://docs.rs/reqwest/), each user maintains its own
143//! web client state, handling cookies and more so your "users" can log in, fill out forms,
144//! and more, as real users on your sites would do.
145//!
146//! ### Running the Swanling load test
147//!
148//! Attempts to run our example will result in an error, as we have not yet defined the
149//! host against which this load test should be run. We intentionally do not hard code the
150//! host in the individual tasks, as this allows us to run the test against different
151//! environments, such as local development, staging, and production.
152//!
153//! ```bash
154//! $ cargo run --release
155//!    Compiling loadtest v0.1.0 (~/loadtest)
156//!     Finished release [optimized] target(s) in 1.52s
157//!      Running `target/release/loadtest`
158//! Error: InvalidOption { option: "--host", value: "", detail: "A host must be defined via the --host option, the SwanlingAttack.set_default() function, or the SwanlingTaskSet.set_host() function (no host defined for WebsiteUser)." }
159//! ```
160//! Pass in the `-h` flag to see all available run-time options. For now, we'll use a few
161//! options to customize our load test.
162//!
163//! ```bash
164//! $ cargo run --release -- --host http://dev.local -t 30s -v
165//! ```
166//!
167//! The first option we specified is `--host`, and in this case tells Swanling to run the load test
168//! against a VM on my local network. The `-t 30s` option tells Swanling to end the load test after 30
169//! seconds (for real load tests you'll certainly want to run it longer, you can use `h`, `m`, and
170//! `s` to specify hours, minutes and seconds respectively. For example, `-t1h30m` would run the
171//! load test for 1 hour 30 minutes). Finally, the `-v` flag tells swanling to display INFO and higher
172//! level logs to stdout, giving more insight into what is happening. (Additional `-v` flags will
173//! result in considerably more debug output, and are not recommended for running actual load tests;
174//! they're only useful if you're trying to debug Swanling itself.)
175//!
176//! Running the test results in the following output (broken up to explain it as it goes):
177//!
178//! ```bash
179//!    Finished release [optimized] target(s) in 0.05s
180//!     Running `target/release/loadtest --host 'http://dev.local' -t 30s -v`
181//! 15:42:23 [ INFO] Output verbosity level: INFO
182//! 15:42:23 [ INFO] Logfile verbosity level: WARN
183//! ```
184//!
185//! If we set the `--log-file` flag, Swanling will write a log file with WARN and higher level logs
186//! as you run the test from (add a `-g` flag to log all INFO and higher level logs).
187//!
188//! ```bash
189//! 15:42:23 [ INFO] concurrent users defaulted to 8 (number of CPUs)
190//! 15:42:23 [ INFO] run_time = 30
191//! 15:42:23 [ INFO] hatch_rate = 1
192//! ```
193//!
194//! Swanling will default to launching 1 user per available CPU core, and will launch them all in
195//! one second. You can change how many users are launched with the `-u` option, and you can
196//! change how many users are launched per second with the `-r` option. For example, `-u30 -r2`
197//! would launch 30 users over 15 seconds (two users per second).
198//!
199//! ```bash
200//! 15:42:23 [ INFO] global host configured: http://dev.local/
201//! 15:42:23 [ INFO] initializing user states...
202//! 15:42:23 [ INFO] launching user 1 from LoadtestTasks...
203//! 15:42:24 [ INFO] launching user 2 from LoadtestTasks...
204//! 15:42:25 [ INFO] launching user 3 from LoadtestTasks...
205//! 15:42:26 [ INFO] launching user 4 from LoadtestTasks...
206//! 15:42:27 [ INFO] launching user 5 from LoadtestTasks...
207//! 15:42:28 [ INFO] launching user 6 from LoadtestTasks...
208//! 15:42:29 [ INFO] launching user 7 from LoadtestTasks...
209//! 15:42:30 [ INFO] launching user 8 from LoadtestTasks...
210//! 15:42:31 [ INFO] launched 8 users...
211//! 15:42:31 [ INFO] printing running metrics after 8 seconds...
212//! ```
213//!
214//! Each user is launched in its own thread with its own user state. Swanling is able to make
215//! very efficient use of server resources. By default Swanling resets the metrics after all
216//! users are launched, but first it outputs the metrics collected while ramping up:
217//!
218//! ```bash
219//! 15:42:31 [ INFO] printing running metrics after 8 seconds...
220//!
221//!  === PER TASK METRICS ===
222//!  ------------------------------------------------------------------------------
223//!  Name                     |   # times run |        # fails |   task/s |  fail/s
224//!  ------------------------------------------------------------------------------
225//!  1: LoadtestTasks         |
226//!    1:                     |         2,033 |         0 (0%) |   254.12 |    0.00
227//!    2: bar                 |           407 |         0 (0%) |    50.88 |    0.00
228//!  -------------------------+---------------+----------------+----------+--------
229//!  Aggregated               |         2,440 |         0 (0%) |   305.00 |    0.00
230//!  ------------------------------------------------------------------------------
231//!  Name                     |    Avg (ms) |        Min |         Max |     Median
232//!  ------------------------------------------------------------------------------
233//!  1: LoadtestTasks         |
234//!    1:                     |       14.23 |          6 |          32 |         14
235//!    2: bar                 |       14.13 |          6 |          30 |         14
236//!  -------------------------+-------------+------------+-------------+-----------
237//!  Aggregated               |       14.21 |          6 |          32 |         14
238//!
239//!  === PER REQUEST METRICS ===
240//!  ------------------------------------------------------------------------------
241//!  Name                     |        # reqs |        # fails |    req/s |  fail/s
242//!  ------------------------------------------------------------------------------
243//!  GET /                    |         2,033 |         0 (0%) |   254.12 |    0.00
244//!  GET bar                  |           407 |         0 (0%) |    50.88 |    0.00
245//!  -------------------------+---------------+----------------+----------+--------
246//!  Aggregated               |         2,440 |         0 (0%) |   305.00 |    0.00
247//!  ------------------------------------------------------------------------------
248//!  Name                     |    Avg (ms) |        Min |        Max |      Median
249//!  ------------------------------------------------------------------------------
250//!  GET /                    |       14.18 |          6 |          32 |         14
251//!  GET bar                  |       14.08 |          6 |          30 |         14
252//!  -------------------------+-------------+------------+-------------+-----------
253//!  Aggregated               |       14.16 |          6 |          32 |         14
254//!
255//! All 8 users hatched, resetting metrics (disable with --no-reset-metrics).
256//! ```
257//!
258//! Swanling can optionally display running metrics if started with `--running-metrics INT`
259//! where INT is an integer value in seconds. For example, if Swanling is started with
260//! `--running-metrics 15` it will display running values approximately every 15 seconds.
261//! Running metrics are broken into several tables. First are the per-task metrics which
262//! are further split into two sections. The first section shows how many requests have
263//! been made, how many of them failed (non-2xx response), and the corresponding per-second
264//! rates.
265//!
266//! This table shows details for all Task Sets and all Tasks defined by your load test,
267//! regardless of if they actually run. This can be useful to ensure that you have set
268//! up weighting as intended, and that you are simulating enough users. As our first
269//! task wasn't named, it just showed up as "1:". Our second task was named, so it shows
270//! up as the name we gave it, "bar".
271//!
272//! ```bash
273//! 15:42:46 [ INFO] printing running metrics after 15 seconds...
274//!
275//!  === PER TASK METRICS ===
276//!  ------------------------------------------------------------------------------
277//!  Name                     |   # times run |        # fails |   task/s |  fail/s
278//!  ------------------------------------------------------------------------------
279//!  1: LoadtestTasks         |
280//!    1:                     |         4,618 |         0 (0%) |   307.87 |    0.00
281//!    2: bar                 |           924 |         0 (0%) |    61.60 |    0.00
282//!  -------------------------+---------------+----------------+----------+--------
283//!  Aggregated               |         5,542 |         0 (0%) |   369.47 |    0.00
284//!  ------------------------------------------------------------------------------
285//!  Name                     |    Avg (ms) |        Min |         Max |     Median
286//!  ------------------------------------------------------------------------------
287//!  1: LoadtestTasks         |
288//!    1:                     |       21.17 |          8 |         151 |         19
289//!    2: bar                 |       21.62 |          9 |         156 |         19
290//!  -------------------------+-------------+------------+-------------+-----------
291//!  Aggregated               |       21.24 |          8 |         156 |         19
292//! ```
293//!
294//! The second table breaks down the same metrics by request instead of by Task. For
295//! our simple load test, each Task only makes a single request, so the metrics are
296//! the same. There are two main differences. First, metrics are listed by request
297//! type and path or name. The first request shows up as `GET /path/to/foo` as the
298//! request was not named. The second request shows up as `GET bar` as the request
299//! was named. The times to complete each are slightly smaller as this is only the
300//! time to make the request, not the time for Swanling to execute the entire task.
301//!
302//! ```bash
303//!  === PER REQUEST METRICS ===
304//!  ------------------------------------------------------------------------------
305//!  Name                     |        # reqs |        # fails |    req/s |  fail/s
306//!  ------------------------------------------------------------------------------
307//!  GET /path/to/foo         |         4,618 |         0 (0%) |   307.87 |    0.00
308//!  GET bar                  |           924 |         0 (0%) |    61.60 |    0.00
309//!  -------------------------+---------------+----------------+----------+--------
310//!  Aggregated               |         5,542 |         0 (0%) |   369.47 |    0.00
311//!  ------------------------------------------------------------------------------
312//!  Name                     |    Avg (ms) |        Min |        Max |      Median
313//!  ------------------------------------------------------------------------------
314//!  GET /path/to/foo         |       21.13 |          8 |         151 |         19
315//!  GET bar                  |       21.58 |          9 |         156 |         19
316//!  -------------------------+-------------+------------+-------------+-----------
317//!  Aggregated               |       21.20 |          8 |         156 |         19
318//! ```
319//!
320//! Note that Swanling respected the per-task weights we set, and `foo` (with a weight of 10)
321//! is being loaded five times as often as `bar` (with a weight of 2). On average
322//! each page is returning within `21.2` milliseconds. The quickest page response was
323//! for `foo` in `8` milliseconds. The slowest page response was for `bar` in `156`
324//! milliseconds.
325//!
326//! ```bash
327//! 15:43:02 [ INFO] stopping after 30 seconds...
328//! 15:43:02 [ INFO] waiting for users to exit
329//! 15:43:02 [ INFO] exiting user 3 from LoadtestTasks...
330//! 15:43:02 [ INFO] exiting user 4 from LoadtestTasks...
331//! 15:43:02 [ INFO] exiting user 5 from LoadtestTasks...
332//! 15:43:02 [ INFO] exiting user 8 from LoadtestTasks...
333//! 15:43:02 [ INFO] exiting user 2 from LoadtestTasks...
334//! 15:43:02 [ INFO] exiting user 7 from LoadtestTasks...
335//! 15:43:02 [ INFO] exiting user 6 from LoadtestTasks...
336//! 15:43:02 [ INFO] exiting user 1 from LoadtestTasks...
337//! 15:43:02 [ INFO] printing metrics after 30 seconds...
338//! ```
339//!
340//! Our example only runs for 30 seconds, so we only see running metrics once. When
341//! the test completes, we get more detail in the final summary. The first two tables
342//! are the same as what we saw earlier, however now they include all metrics for the
343//! entire length of the load test:
344//!
345//! ```bash
346//!  === PER TASK METRICS ===
347//!  ------------------------------------------------------------------------------
348//!  Name                     |   # times run |        # fails |   task/s |  fail/s
349//!  ------------------------------------------------------------------------------
350//!  1: LoadtestTasks         |
351//!    1:                     |         9,974 |         0 (0%) |   332.47 |    0.00
352//!    2: bar                 |         1,995 |         0 (0%) |    66.50 |    0.00
353//!  -------------------------+---------------+----------------+----------+--------
354//!  Aggregated               |        11,969 |         0 (0%) |   398.97 |    0.00
355//!  ------------------------------------------------------------------------------
356//!  Name                     |    Avg (ms) |        Min |         Max |     Median
357//!  ------------------------------------------------------------------------------
358//!  1: LoadtestTasks         |
359//!    1:                     |       19.65 |          8 |         151 |         18
360//!    2: bar                 |       19.92 |          9 |         156 |         18
361//!  -------------------------+-------------+------------+-------------+-----------
362//!  Aggregated               |       19.69 |          8 |         156 |         18
363//!
364//!  === PER REQUEST METRICS ===
365//!  ------------------------------------------------------------------------------
366//!  Name                     |        # reqs |        # fails |    req/s |  fail/s
367//!  ------------------------------------------------------------------------------
368//!  GET /                    |         9,974 |         0 (0%) |   332.47 |    0.00
369//!  GET bar                  |         1,995 |         0 (0%) |    66.50 |    0.00
370//!  -------------------------+---------------+----------------+----------+--------
371//!  Aggregated               |        11,969 |         0 (0%) |   398.97 |    0.00
372//!  ------------------------------------------------------------------------------
373//!  Name                     |    Avg (ms) |        Min |        Max |      Median
374//!  ------------------------------------------------------------------------------
375//!  GET /                    |       19.61 |          8 |         151 |         18
376//!  GET bar                  |       19.88 |          9 |         156 |         18
377//!  -------------------------+-------------+------------+-------------+-----------
378//!  Aggregated               |       19.66 |          8 |         156 |         18
379//!  ------------------------------------------------------------------------------
380//! ```
381//!
382//! The ratio between `foo` and `bar` remained 5:2 as expected.
383//!
384//! ```bash
385//!  ------------------------------------------------------------------------------
386//!  Slowest page load within specified percentile of requests (in ms):
387//!  ------------------------------------------------------------------------------
388//!  Name                     |    50% |    75% |    98% |    99% |  99.9% | 99.99%
389//!  ------------------------------------------------------------------------------
390//!  GET /                    |     18 |     21 |     29 |     79 |    140 |    140
391//!  GET bar                  |     18 |     21 |     29 |    120 |    150 |    150
392//!  -------------------------+--------+--------+--------+--------+--------+-------
393//!  Aggregated               |     18 |     21 |     29 |     84 |    140 |    156
394//! ```
395//!
396//! A new table shows additional information, breaking down response-time by
397//! percentile. This shows that the slowest page loads only happened in the
398//! slowest 1% of page loads, so were an edge case. 98% of the time page loads
399//! happened in 29 milliseconds or less.
400//!
401//! ## License
402//!
403//! Copyright 2020-21 Jeremy Andrews
404//!
405//! Licensed under the Apache License, Version 2.0 (the "License");
406//! you may not use this file except in compliance with the License.
407//! You may obtain a copy of the License at
408//!
409//! [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)
410//!
411//! Unless required by applicable law or agreed to in writing, software
412//! distributed under the License is distributed on an "AS IS" BASIS,
413//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
414//! See the License for the specific language governing permissions and
415//! limitations under the License.
416
417#[macro_use]
418extern crate log;
419
420pub mod controller;
421pub mod logger;
422#[cfg(feature = "gaggle")]
423mod manager;
424pub mod metrics;
425pub mod prelude;
426mod report;
427pub mod swanling;
428mod throttle;
429mod user;
430pub mod util;
431#[cfg(feature = "gaggle")]
432mod worker;
433
434use chrono::prelude::*;
435use gumdrop::Options;
436use lazy_static::lazy_static;
437#[cfg(feature = "gaggle")]
438use nng::Socket;
439use rand::seq::SliceRandom;
440use rand::thread_rng;
441use serde::{Deserialize, Serialize};
442use simplelog::*;
443use std::collections::hash_map::DefaultHasher;
444use std::collections::BTreeMap;
445use std::hash::{Hash, Hasher};
446use std::path::PathBuf;
447use std::sync::{
448    atomic::{AtomicBool, AtomicUsize, Ordering},
449    Arc,
450};
451use std::{fmt, io, time};
452use tokio::fs::File;
453use tokio::runtime::Runtime;
454
455use crate::controller::{SwanlingControllerProtocol, SwanlingControllerRequest};
456use crate::logger::{SwanlingLogFormat, SwanlingLoggerJoinHandle, SwanlingLoggerTx};
457use crate::metrics::{SwanlingCoordinatedOmissionMitigation, SwanlingMetric, SwanlingMetrics};
458use crate::swanling::{
459    GaggleUser, SwanlingTask, SwanlingTaskSet, SwanlingUser, SwanlingUserCommand,
460};
461#[cfg(feature = "gaggle")]
462use crate::worker::{register_shutdown_pipe_handler, GaggleMetrics};
463
464/// Constant defining Swanling's default port when running a Regatta.
465const DEFAULT_PORT: &str = "5115";
466
467/// Constant defining Swanling's default telnet Controller port.
468const DEFAULT_TELNET_PORT: &str = "5116";
469
470/// Constant defining Swanling's default WebSocket Controller port.
471const DEFAULT_WEBSOCKET_PORT: &str = "5117";
472
473// WORKER_ID is only used when running a gaggle (a distributed load test).
474lazy_static! {
475    static ref WORKER_ID: AtomicUsize = AtomicUsize::new(0);
476}
477
478/// Internal representation of a weighted task list.
479type WeightedSwanlingTasks = Vec<(usize, String)>;
480
481/// Internal representation of unsequenced tasks.
482type UnsequencedSwanlingTasks = Vec<SwanlingTask>;
483/// Internal representation of sequenced tasks.
484type SequencedSwanlingTasks = BTreeMap<usize, Vec<SwanlingTask>>;
485
486/// Returns the unique identifier of the running Worker when running in Regatta mode.
487///
488/// The first Worker to connect to the Manager is assigned an ID of 1. For each
489/// subsequent Worker to connect to the Manager the ID is incremented by 1. This
490/// identifier is primarily an aid in tracing logs.
491pub fn get_worker_id() -> usize {
492    WORKER_ID.load(Ordering::Relaxed)
493}
494
495#[cfg(not(feature = "gaggle"))]
496#[derive(Debug, Clone)]
497/// Socket used for coordinating a Regatta distributed load test.
498pub(crate) struct Socket {}
499
500/// An enumeration of all errors a [`SwanlingAttack`](./struct.SwanlingAttack.html) can return.
501#[derive(Debug)]
502pub enum SwanlingError {
503    /// Wraps a [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html).
504    Io(io::Error),
505    /// Wraps a [`reqwest::Error`](https://docs.rs/reqwest/*/reqwest/struct.Error.html).
506    Reqwest(reqwest::Error),
507    /// Wraps a ['tokio::task::JoinError'](https://tokio-rs.github.io/tokio/doc/tokio/task/struct.JoinError.html).
508    TokioJoin(tokio::task::JoinError),
509    //std::convert::From<tokio::task::JoinError>
510    /// Failed attempt to use code that requires a compile-time feature be enabled.
511    FeatureNotEnabled {
512        /// The missing compile-time feature.
513        feature: String,
514        /// An optional explanation of the error.
515        detail: String,
516    },
517    /// Failed to parse a hostname.
518    InvalidHost {
519        /// The invalid hostname that caused this error.
520        host: String,
521        /// An optional explanation of the error.
522        detail: String,
523        /// Wraps a [`url::ParseError`](https://docs.rs/url/*/url/enum.ParseError.html).
524        parse_error: url::ParseError,
525    },
526    /// Invalid option or value specified, may only be invalid in context.
527    InvalidOption {
528        /// The invalid option that caused this error, may be only invalid in context.
529        option: String,
530        /// The invalid value that caused this error, may be only invalid in context.
531        value: String,
532        /// An optional explanation of the error.
533        detail: String,
534    },
535    /// Invalid wait time specified.
536    InvalidWaitTime {
537        // The specified minimum wait time.
538        min_wait: usize,
539        // The specified maximum wait time.
540        max_wait: usize,
541        /// An optional explanation of the error.
542        detail: String,
543    },
544    /// Invalid weight specified.
545    InvalidWeight {
546        // The specified weight.
547        weight: usize,
548        /// An optional explanation of the error.
549        detail: String,
550    },
551    /// [`SwanlingAttack`](./struct.SwanlingAttack.html) has no [`SwanlingTaskSet`](./swanling/struct.SwanlingTaskSet.html) defined.
552    NoTaskSets {
553        /// An optional explanation of the error.
554        detail: String,
555    },
556}
557/// Implement a helper to provide a text description of all possible types of errors.
558impl SwanlingError {
559    fn describe(&self) -> &str {
560        match *self {
561            SwanlingError::Io(_) => "io::Error",
562            SwanlingError::Reqwest(_) => "reqwest::Error",
563            SwanlingError::TokioJoin(_) => "tokio::task::JoinError",
564            SwanlingError::FeatureNotEnabled { .. } => "required compile-time feature not enabled",
565            SwanlingError::InvalidHost { .. } => "failed to parse hostname",
566            SwanlingError::InvalidOption { .. } => "invalid option or value specified",
567            SwanlingError::InvalidWaitTime { .. } => "invalid wait_time specified",
568            SwanlingError::InvalidWeight { .. } => "invalid weight specified",
569            SwanlingError::NoTaskSets { .. } => "no task sets defined",
570        }
571    }
572}
573
574/// Implement format trait to allow displaying errors.
575impl fmt::Display for SwanlingError {
576    // Implement display of error with `{}` marker.
577    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
578        match *self {
579            SwanlingError::Io(ref source) => {
580                write!(f, "SwanlingError: {} ({})", self.describe(), source)
581            }
582            SwanlingError::Reqwest(ref source) => {
583                write!(f, "SwanlingError: {} ({})", self.describe(), source)
584            }
585            SwanlingError::TokioJoin(ref source) => {
586                write!(f, "SwanlingError: {} ({})", self.describe(), source)
587            }
588            SwanlingError::InvalidHost {
589                ref parse_error, ..
590            } => write!(f, "SwanlingError: {} ({})", self.describe(), parse_error),
591            _ => write!(f, "SwanlingError: {}", self.describe()),
592        }
593    }
594}
595
596// Define the lower level source of this error, if any.
597impl std::error::Error for SwanlingError {
598    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
599        match *self {
600            SwanlingError::Io(ref source) => Some(source),
601            SwanlingError::Reqwest(ref source) => Some(source),
602            SwanlingError::TokioJoin(ref source) => Some(source),
603            SwanlingError::InvalidHost {
604                ref parse_error, ..
605            } => Some(parse_error),
606            _ => None,
607        }
608    }
609}
610
611/// Auto-convert Reqwest errors.
612impl From<reqwest::Error> for SwanlingError {
613    fn from(err: reqwest::Error) -> SwanlingError {
614        SwanlingError::Reqwest(err)
615    }
616}
617
618/// Auto-convert IO errors.
619impl From<io::Error> for SwanlingError {
620    fn from(err: io::Error) -> SwanlingError {
621        SwanlingError::Io(err)
622    }
623}
624
625/// Auto-convert TokioJoin errors.
626impl From<tokio::task::JoinError> for SwanlingError {
627    fn from(err: tokio::task::JoinError) -> SwanlingError {
628        SwanlingError::TokioJoin(err)
629    }
630}
631
632#[derive(Clone, Debug, PartialEq)]
633/// A [`SwanlingAttack`](./struct.SwanlingAttack.html) load test operates in one (and only one)
634/// of the following modes.
635pub enum AttackMode {
636    /// During early startup before one of the following modes gets assigned.
637    Undefined,
638    /// A single standalone process performing a load test.
639    StandAlone,
640    /// The controlling process in a Regatta distributed load test.
641    Manager,
642    /// One of one or more working processes in a Regatta distributed load test.
643    Worker,
644}
645
646#[derive(Clone, Debug, PartialEq)]
647/// A [`SwanlingAttack`](./struct.SwanlingAttack.html) load test moves through each of the following
648/// phases during a complete load test.
649pub enum AttackPhase {
650    /// No load test is running, configuration can be changed by a Controller.
651    Idle,
652    /// [`SwanlingUser`](./swanling/struct.SwanlingUser.html)s are launching and beginning to generate
653    /// load.
654    Starting,
655    /// All [`SwanlingUser`](./swanling/struct.SwanlingUser.html)s have launched and are generating load.
656    Running,
657    /// [`SwanlingUser`](./swanling/struct.SwanlingUser.html)s are stopping.
658    Stopping,
659    /// Exiting the load test.
660    Shutdown,
661}
662
663#[derive(Clone, Debug, PartialEq)]
664/// Used to define the order [`SwanlingTaskSet`](./swanling/struct.SwanlingTaskSet.html)s and
665/// [`SwanlingTask`](./swanling/struct.SwanlingTask.html)s are allocated.
666///
667/// In order to configure the scheduler, and to see examples of the different scheduler
668/// variants, review the
669/// [`SwanlingAttack::set_scheduler`](./struct.SwanlingAttack.html#method.set_scheduler)
670/// documentation.
671pub enum SwanlingScheduler {
672    /// Allocate one of each available type at a time (default).
673    RoundRobin,
674    /// Allocate in the order and weighting defined.
675    Serial,
676    /// Allocate in a random order.
677    Random,
678}
679
680/// Optional default values for Swanling run-time options.
681#[derive(Clone, Debug, Default)]
682pub struct SwanlingDefaults {
683    /// An optional default host to run this load test against.
684    host: Option<String>,
685    /// An optional default number of users to simulate.
686    users: Option<usize>,
687    /// An optional default number of clients to start per second.
688    hatch_rate: Option<String>,
689    /// An optional default number of seconds for the test to run.
690    run_time: Option<usize>,
691    /// An optional default log level.
692    log_level: Option<u8>,
693    /// An optional default for the swanling log file name.
694    swanling_log: Option<String>,
695    /// An optional default value for verbosity level.
696    verbose: Option<u8>,
697    /// An optional default for printing running metrics.
698    running_metrics: Option<usize>,
699    /// An optional default for not resetting metrics after all users started.
700    no_reset_metrics: Option<bool>,
701    /// An optional default for not tracking metrics.
702    no_metrics: Option<bool>,
703    /// An optional default for not tracking task metrics.
704    no_task_metrics: Option<bool>,
705    /// An optional default for not displaying an error summary.
706    no_error_summary: Option<bool>,
707    /// An optional default for the html-formatted report file name.
708    report_file: Option<String>,
709    /// An optional default for the requests log file name.
710    request_log: Option<String>,
711    /// An optional default for the requests log file format.
712    request_format: Option<SwanlingLogFormat>,
713    /// An optional default for the tasks log file name.
714    task_log: Option<String>,
715    /// An optional default for the tasks log file format.
716    task_format: Option<SwanlingLogFormat>,
717    /// An optional default for the error log file name.
718    error_log: Option<String>,
719    /// An optional default for the error log format.
720    error_format: Option<SwanlingLogFormat>,
721    /// An optional default for the debug log file name.
722    debug_log: Option<String>,
723    /// An optional default for the debug log format.
724    debug_format: Option<SwanlingLogFormat>,
725    /// An optional default for not logging response body in debug log.
726    no_debug_body: Option<bool>,
727    /// An optional default for not enabling telnet Controller thread.
728    no_telnet: Option<bool>,
729    /// An optional default for not enabling WebSocket Controller thread.
730    no_websocket: Option<bool>,
731    /// An optional default for not auto-starting the load test.
732    no_autostart: Option<bool>,
733    /// An optional default for coordinated omission mitigation.
734    co_mitigation: Option<SwanlingCoordinatedOmissionMitigation>,
735    /// An optional default to track additional status code metrics.
736    status_codes: Option<bool>,
737    /// An optional default maximum requests per second.
738    throttle_requests: Option<usize>,
739    /// An optional default to follows base_url redirect with subsequent request.
740    sticky_follow: Option<bool>,
741    /// An optional default to enable Manager mode.
742    manager: Option<bool>,
743    /// An optional default for number of Workers to expect.
744    expect_workers: Option<u16>,
745    /// An optional default for Manager to ignore load test checksum.
746    no_hash_check: Option<bool>,
747    /// An optional default for host telnet Controller listens on.
748    telnet_host: Option<String>,
749    /// An optional default for port telnet Controller listens on.
750    telnet_port: Option<u16>,
751    /// An optional default for host WebSocket Controller listens on.
752    websocket_host: Option<String>,
753    /// An optional default for port WebSocket Controller listens on.
754    websocket_port: Option<u16>,
755    /// An optional default for host Manager listens on.
756    manager_bind_host: Option<String>,
757    /// An optional default for port Manager listens on.
758    manager_bind_port: Option<u16>,
759    /// An optional default to enable Worker mode.
760    worker: Option<bool>,
761    /// An optional default for host Worker connects to.
762    manager_host: Option<String>,
763    /// An optional default for port Worker connects to.
764    manager_port: Option<u16>,
765}
766
767/// Allows the optional configuration of Swanling's defaults.
768#[derive(Debug)]
769pub enum SwanlingDefault {
770    /// An optional default host to run this load test against.
771    Host,
772    /// An optional default number of users to simulate.
773    Users,
774    /// An optional default number of clients to start per second.
775    HatchRate,
776    /// An optional default number of seconds for the test to run.
777    RunTime,
778    /// An optional default log level.
779    LogLevel,
780    /// An optional default for the log file name.
781    SwanlingLog,
782    /// An optional default value for verbosity level.
783    Verbose,
784    /// An optional default for printing running metrics.
785    RunningMetrics,
786    /// An optional default for not resetting metrics after all users started.
787    NoResetMetrics,
788    /// An optional default for not tracking metrics.
789    NoMetrics,
790    /// An optional default for not tracking task metrics.
791    NoTaskMetrics,
792    /// An optional default for not displaying an error summary.
793    NoErrorSummary,
794    /// An optional default for the report file name.
795    ReportFile,
796    /// An optional default for the request log file name.
797    RequestLog,
798    /// An optional default for the request log file format.
799    RequestFormat,
800    /// An optional default for the task log file name.
801    TaskLog,
802    /// An optional default for the task log file format.
803    TaskFormat,
804    /// An optional default for the error log file name.
805    ErrorLog,
806    /// An optional default for the error log format.
807    ErrorFormat,
808    /// An optional default for the debug log file name.
809    DebugLog,
810    /// An optional default for the debug log format.
811    DebugFormat,
812    /// An optional default for not logging the response body in the debug log.
813    NoDebugBody,
814    /// An optional default for not enabling telnet Controller thread.
815    NoTelnet,
816    /// An optional default for not enabling WebSocket Controller thread.
817    NoWebSocket,
818    /// An optional default for coordinated omission mitigation.
819    CoordinatedOmissionMitigation,
820    /// An optional default for not automatically starting load test.
821    NoAutoStart,
822    /// An optional default to track additional status code metrics.
823    StatusCodes,
824    /// An optional default maximum requests per second.
825    ThrottleRequests,
826    /// An optional default to follows base_url redirect with subsequent request.
827    StickyFollow,
828    /// An optional default to enable Manager mode.
829    Manager,
830    /// An optional default for number of Workers to expect.
831    ExpectWorkers,
832    /// An optional default for Manager to ignore load test checksum.
833    NoHashCheck,
834    /// An optional default for host telnet Controller listens on.
835    TelnetHost,
836    /// An optional default for port telnet Controller listens on.
837    TelnetPort,
838    /// An optional default for host Websocket Controller listens on.
839    WebSocketHost,
840    /// An optional default for port WebSocket Controller listens on.
841    WebSocketPort,
842    /// An optional default for host Manager listens on.
843    ManagerBindHost,
844    /// An optional default for port Manager listens on.
845    ManagerBindPort,
846    /// An optional default to enable Worker mode.
847    Worker,
848    /// An optional default for host Worker connects to.
849    ManagerHost,
850    /// An optional default for port Worker connects to.
851    ManagerPort,
852}
853
854#[derive(Debug)]
855/// Internal global run state for load test.
856struct SwanlingAttackRunState {
857    /// A timestamp tracking when the previous [`SwanlingUser`](./swanling/struct.SwanlingUser.html)
858    /// was launched.
859    spawn_user_timer: std::time::Instant,
860    /// How many milliseconds until the next [`SwanlingUser`](./swanling/struct.SwanlingUser.html)
861    /// should be spawned.
862    spawn_user_in_ms: usize,
863    /// A counter tracking which [`SwanlingUser`](./swanling/struct.SwanlingUser.html) is being
864    /// spawned.
865    spawn_user_counter: usize,
866    /// This variable accounts for time spent doing things which is then subtracted from
867    /// the time sleeping to avoid an unintentional drift in events that are supposed to
868    /// happen regularly.
869    drift_timer: tokio::time::Instant,
870    /// Unbounded sender used by all [`SwanlingUser`](./swanling/struct.SwanlingUser.html)
871    /// threads to send metrics to parent.
872    all_threads_metrics_tx: flume::Sender<SwanlingMetric>,
873    /// Unbounded receiver used by Swanling parent to receive metrics from
874    /// [`SwanlingUser`](./swanling/struct.SwanlingUser.html)s.
875    metrics_rx: flume::Receiver<SwanlingMetric>,
876    /// Optional unbounded receiver for logger thread, if enabled.
877    logger_handle: SwanlingLoggerJoinHandle,
878    /// Optional unbounded sender from all [`SwanlingUser`](./swanling/struct.SwanlingUser.html)s
879    /// to logger thread, if enabled.
880    all_threads_logger_tx: SwanlingLoggerTx,
881    /// Optional receiver for all [`SwanlingUser`](./swanling/struct.SwanlingUser.html)s from
882    /// throttle thread, if enabled.
883    throttle_threads_tx: Option<flume::Sender<bool>>,
884    /// Optional sender for throttle thread, if enabled.
885    parent_to_throttle_tx: Option<flume::Sender<bool>>,
886    /// Optional channel allowing controller thread to make requests, if not disabled.
887    controller_channel_rx: Option<flume::Receiver<SwanlingControllerRequest>>,
888    /// Optional unbuffered writer for html-formatted report file, if enabled.
889    report_file: Option<File>,
890    /// A flag tracking whether or not the header has been written when the metrics
891    /// log is enabled.
892    metrics_header_displayed: bool,
893    /// When entering the idle phase use this flag to only display a message one time.
894    idle_status_displayed: bool,
895    /// Collection of all [`SwanlingUser`](./swanling/struct.SwanlingUser.html) threads so they
896    /// can be stopped later.
897    users: Vec<tokio::task::JoinHandle<()>>,
898    /// All unbounded senders to allow communication with
899    /// [`SwanlingUser`](./swanling/struct.SwanlingUser.html) threads.
900    user_channels: Vec<flume::Sender<SwanlingUserCommand>>,
901    /// Timer tracking when to display running metrics, if enabled.
902    running_metrics_timer: std::time::Instant,
903    /// Boolean flag indicating if running metrics should be displayed.
904    display_running_metrics: bool,
905    /// Boolean flag indicating if all [`SwanlingUser`](./swanling/struct.SwanlingUser.html)s
906    /// have been spawned.
907    all_users_spawned: bool,
908    /// Boolean flag indicating of Swanling should shutdown after stopping a running load test.
909    shutdown_after_stop: bool,
910    /// Thread-safe boolean flag indicating if the [`SwanlingAttack`](./struct.SwanlingAttack.html)
911    /// has been canceled.
912    canceled: Arc<AtomicBool>,
913    /// Optional socket used to coordinate a distributed Regatta.
914    socket: Option<Socket>,
915}
916
917/// Global internal state for the load test.
918#[derive(Clone)]
919pub struct SwanlingAttack {
920    /// An optional task that is run one time before starting SwanlingUsers and running SwanlingTaskSets.
921    test_start_task: Option<SwanlingTask>,
922    /// An optional task that is run one time after all SwanlingUsers have finished.
923    test_stop_task: Option<SwanlingTask>,
924    /// A vector containing one copy of each SwanlingTaskSet defined by this load test.
925    task_sets: Vec<SwanlingTaskSet>,
926    /// A weighted vector containing a SwanlingUser object for each SwanlingUser that will run during this load test.
927    weighted_users: Vec<SwanlingUser>,
928    /// A weighted vector containing a lightweight GaggleUser object that is sent to all Workers if running in Regatta mode.
929    weighted_gaggle_users: Vec<GaggleUser>,
930    /// Optional default values for Swanling run-time options.
931    defaults: SwanlingDefaults,
932    /// Configuration object holding options set when launching the load test.
933    configuration: SwanlingConfiguration,
934    /// How long (in seconds) the load test should run.
935    run_time: usize,
936    /// The load test operates in only one of the following modes: StandAlone, Manager, or Worker.
937    attack_mode: AttackMode,
938    /// Which phase the load test is currently operating in.
939    attack_phase: AttackPhase,
940    /// Defines the order [`SwanlingTaskSet`](./swanling/struct.SwanlingTaskSet.html)s and
941    /// [`SwanlingTask`](./swanling/struct.SwanlingTask.html)s are allocated.
942    scheduler: SwanlingScheduler,
943    /// When the load test started.
944    started: Option<time::Instant>,
945    /// All metrics merged together.
946    metrics: SwanlingMetrics,
947}
948/// Swanling's internal global state.
949impl SwanlingAttack {
950    /// Load configuration and initialize a [`SwanlingAttack`](./struct.SwanlingAttack.html).
951    ///
952    /// # Example
953    /// ```rust
954    /// use swanling::prelude::*;
955    ///
956    /// let mut swanling_attack = SwanlingAttack::initialize();
957    /// ```
958    pub fn initialize() -> Result<SwanlingAttack, SwanlingError> {
959        Ok(SwanlingAttack {
960            test_start_task: None,
961            test_stop_task: None,
962            task_sets: Vec::new(),
963            weighted_users: Vec::new(),
964            weighted_gaggle_users: Vec::new(),
965            defaults: SwanlingDefaults::default(),
966            configuration: SwanlingConfiguration::parse_args_default_or_exit(),
967            run_time: 0,
968            attack_mode: AttackMode::Undefined,
969            attack_phase: AttackPhase::Idle,
970            scheduler: SwanlingScheduler::RoundRobin,
971            started: None,
972            metrics: SwanlingMetrics::default(),
973        })
974    }
975
976    /// Initialize a [`SwanlingAttack`](./struct.SwanlingAttack.html) with an already loaded
977    /// configuration.
978    ///
979    /// This is generally used by Worker instances and tests.
980    ///
981    /// # Example
982    /// ```rust
983    /// use swanling::{SwanlingAttack, SwanlingConfiguration};
984    /// use gumdrop::Options;
985    ///
986    /// let configuration = SwanlingConfiguration::parse_args_default_or_exit();
987    /// let mut swanling_attack = SwanlingAttack::initialize_with_config(configuration);
988    /// ```
989    pub fn initialize_with_config(
990        configuration: SwanlingConfiguration,
991    ) -> Result<SwanlingAttack, SwanlingError> {
992        Ok(SwanlingAttack {
993            test_start_task: None,
994            test_stop_task: None,
995            task_sets: Vec::new(),
996            weighted_users: Vec::new(),
997            weighted_gaggle_users: Vec::new(),
998            defaults: SwanlingDefaults::default(),
999            configuration,
1000            run_time: 0,
1001            attack_mode: AttackMode::Undefined,
1002            attack_phase: AttackPhase::Idle,
1003            scheduler: SwanlingScheduler::RoundRobin,
1004            started: None,
1005            metrics: SwanlingMetrics::default(),
1006        })
1007    }
1008
1009    /// Optionally initialize the logger which writes to standard out and/or to
1010    /// a configurable log file.
1011    ///
1012    /// This method is invoked by
1013    /// [`SwanlingAttack.execute()`](./struct.SwanlingAttack.html#method.execute).
1014    pub(crate) fn initialize_logger(&self) {
1015        // Allow optionally controlling debug output level
1016        let debug_level;
1017        match self.configuration.verbose {
1018            0 => debug_level = LevelFilter::Warn,
1019            1 => debug_level = LevelFilter::Info,
1020            2 => debug_level = LevelFilter::Debug,
1021            _ => debug_level = LevelFilter::Trace,
1022        }
1023
1024        // Set log level based on run-time option or default if set.
1025        let log_level_value = if self.configuration.log_level > 0 {
1026            self.configuration.log_level
1027        } else if let Some(default_log_level) = self.defaults.log_level {
1028            default_log_level
1029        } else {
1030            0
1031        };
1032        let log_level = match log_level_value {
1033            0 => LevelFilter::Warn,
1034            1 => LevelFilter::Info,
1035            2 => LevelFilter::Debug,
1036            _ => LevelFilter::Trace,
1037        };
1038
1039        let swanling_log: Option<PathBuf>;
1040        // Use --log-file if set.
1041        if !self.configuration.swanling_log.is_empty() {
1042            swanling_log = Some(PathBuf::from(&self.configuration.swanling_log));
1043        }
1044        // Otherwise use swanling_attack.defaults.swanling_log if set.
1045        else if let Some(default_swanling_log) = &self.defaults.swanling_log {
1046            swanling_log = Some(PathBuf::from(default_swanling_log));
1047        }
1048        // Otherwise disable the log.
1049        else {
1050            swanling_log = None;
1051        }
1052
1053        if let Some(log_to_file) = swanling_log {
1054            match CombinedLogger::init(vec![
1055                SimpleLogger::new(debug_level, Config::default()),
1056                WriteLogger::new(
1057                    log_level,
1058                    Config::default(),
1059                    std::fs::File::create(&log_to_file).unwrap(),
1060                ),
1061            ]) {
1062                Ok(_) => (),
1063                Err(e) => {
1064                    info!("failed to initialize CombinedLogger: {}", e);
1065                }
1066            }
1067            info!("Writing to log file: {}", log_to_file.display());
1068        } else {
1069            match CombinedLogger::init(vec![SimpleLogger::new(debug_level, Config::default())]) {
1070                Ok(_) => (),
1071                Err(e) => {
1072                    info!("failed to initialize CombinedLogger: {}", e);
1073                }
1074            }
1075        }
1076
1077        info!("Output verbosity level: {}", debug_level);
1078        info!("Logfile verbosity level: {}", log_level);
1079    }
1080
1081    /// Define the order [`SwanlingTaskSet`](./swanling/struct.SwanlingTaskSet.html)s are
1082    /// allocated to new [`SwanlingUser`](./swanling/struct.SwanlingUser.html)s as they are
1083    /// launched.
1084    ///
1085    /// By default, [`SwanlingTaskSet`](./swanling/struct.SwanlingTaskSet.html)s are allocated
1086    /// to new [`SwanlingUser`](./swanling/struct.SwanlingUser.html)s in a round robin style.
1087    /// For example, if TaskSet A has a weight of 5, TaskSet B has a weight of 3, and
1088    /// you launch 20 users, they will be launched in the following order:
1089    ///  A, B, A, B, A, B, A, A, A, B, A, B, A, B, A, A, A, B, A, B
1090    ///
1091    /// Note that the following pattern is repeated:
1092    ///  A, B, A, B, A, B, A, A
1093    ///
1094    /// If reconfigured to schedule serially, then they will instead be allocated in
1095    /// the following order:
1096    ///  A, A, A, A, A, B, B, B, A, A, A, A, A, B, B, B, A, A, A, A
1097    ///
1098    /// In the serial case, the following pattern is repeated:
1099    ///  A, A, A, A, A, B, B, B
1100    ///
1101    /// In the following example, [`SwanlingTaskSet`](./swanling/struct.SwanlingTaskSet.html)s
1102    /// are allocated to launching [`SwanlingUser`](./swanling/struct.SwanlingUser.html)s in a
1103    /// random order. This means running the test multiple times can generate
1104    /// different amounts of load, as depending on your weighting rules you may
1105    /// have a different number of [`SwanlingUser`](./swanling/struct.SwanlingUser.html)s
1106    /// running each [`SwanlingTaskSet`](./swanling/struct.SwanlingTaskSet.html) each time.
1107    ///
1108    /// # Example
1109    /// ```rust
1110    /// use swanling::prelude::*;
1111    ///
1112    /// fn main() -> Result<(), SwanlingError> {
1113    ///     SwanlingAttack::initialize()?
1114    ///         .set_scheduler(SwanlingScheduler::Random)
1115    ///         .register_taskset(taskset!("A Tasks")
1116    ///             .set_weight(5)?
1117    ///             .register_task(task!(a_task_1))
1118    ///         )
1119    ///         .register_taskset(taskset!("B Tasks")
1120    ///             .set_weight(3)?
1121    ///             .register_task(task!(b_task_1))
1122    ///         );
1123    ///
1124    ///     Ok(())
1125    /// }
1126    ///
1127    /// async fn a_task_1(user: &SwanlingUser) -> SwanlingTaskResult {
1128    ///     let _swanling = user.get("/foo").await?;
1129    ///
1130    ///     Ok(())
1131    /// }
1132    ///
1133    /// async fn b_task_1(user: &SwanlingUser) -> SwanlingTaskResult {
1134    ///     let _swanling = user.get("/bar").await?;
1135    ///
1136    ///     Ok(())
1137    /// }
1138    /// ```
1139    pub fn set_scheduler(mut self, scheduler: SwanlingScheduler) -> Self {
1140        self.scheduler = scheduler;
1141        self
1142    }
1143
1144    /// A load test must contain one or more [`SwanlingTaskSet`](./swanling/struct.SwanlingTaskSet.html)s
1145    /// be registered into Swanling's global state with this method for it to run.
1146    ///
1147    /// # Example
1148    /// ```rust
1149    /// use swanling::prelude::*;
1150    ///
1151    /// fn main() -> Result<(), SwanlingError> {
1152    ///     SwanlingAttack::initialize()?
1153    ///         .register_taskset(taskset!("ExampleTasks")
1154    ///             .register_task(task!(example_task))
1155    ///         )
1156    ///         .register_taskset(taskset!("OtherTasks")
1157    ///             .register_task(task!(other_task))
1158    ///         );
1159    ///
1160    ///     Ok(())
1161    /// }
1162    ///
1163    /// async fn example_task(user: &SwanlingUser) -> SwanlingTaskResult {
1164    ///     let _swanling = user.get("/foo").await?;
1165    ///
1166    ///     Ok(())
1167    /// }
1168    ///
1169    /// async fn other_task(user: &SwanlingUser) -> SwanlingTaskResult {
1170    ///     let _swanling = user.get("/bar").await?;
1171    ///
1172    ///     Ok(())
1173    /// }
1174    /// ```
1175    pub fn register_taskset(mut self, mut taskset: SwanlingTaskSet) -> Self {
1176        taskset.task_sets_index = self.task_sets.len();
1177        self.task_sets.push(taskset);
1178        self
1179    }
1180
1181    /// Optionally define a task to run before users are started and all task sets
1182    /// start running. This is would generally be used to set up anything required
1183    /// for the load test.
1184    ///
1185    /// The [`SwanlingUser`](./swanling/struct.SwanlingUser.html) used to run the `test_start`
1186    /// tasks is not preserved and does not otherwise affect the subsequent
1187    /// [`SwanlingUser`](./swanling/struct.SwanlingUser.html)s that run the rest of the load
1188    /// test. For example, if the [`SwanlingUser`](./swanling/struct.SwanlingUser.html)
1189    /// logs in during `test_start`, subsequent [`SwanlingUser`](./swanling/struct.SwanlingUser.html)
1190    /// do not retain this session and are therefor not already logged in.
1191    ///
1192    /// When running in a distributed Regatta, this task is only run one time by the
1193    /// Manager.
1194    ///
1195    /// # Example
1196    /// ```rust
1197    /// use swanling::prelude::*;
1198    ///
1199    /// fn main() -> Result<(), SwanlingError> {
1200    ///     SwanlingAttack::initialize()?
1201    ///         .test_start(task!(setup));
1202    ///
1203    ///     Ok(())
1204    /// }
1205    ///
1206    /// async fn setup(user: &SwanlingUser) -> SwanlingTaskResult {
1207    ///     // do stuff to set up load test ...
1208    ///
1209    ///     Ok(())
1210    /// }
1211    /// ```
1212    pub fn test_start(mut self, task: SwanlingTask) -> Self {
1213        self.test_start_task = Some(task);
1214        self
1215    }
1216
1217    /// Optionally define a task to run after all users have finished running
1218    /// all defined task sets. This would generally be used to clean up anything
1219    /// that was specifically set up for the load test.
1220    ///
1221    /// When running in a distributed Regatta, this task is only run one time by the
1222    /// Manager.
1223    ///
1224    /// # Example
1225    /// ```rust
1226    /// use swanling::prelude::*;
1227    ///
1228    /// fn main() -> Result<(), SwanlingError> {
1229    ///     SwanlingAttack::initialize()?
1230    ///         .test_stop(task!(teardown));
1231    ///
1232    ///     Ok(())
1233    /// }
1234    ///
1235    /// async fn teardown(user: &SwanlingUser) -> SwanlingTaskResult {
1236    ///     // do stuff to tear down the load test ...
1237    ///
1238    ///     Ok(())
1239    /// }
1240    /// ```
1241    pub fn test_stop(mut self, task: SwanlingTask) -> Self {
1242        self.test_stop_task = Some(task);
1243        self
1244    }
1245
1246    /// Use configured SwanlingScheduler to build out a properly weighted list of
1247    /// [`SwanlingTaskSet`](./swanling/struct.SwanlingTaskSet.html)s to be assigned to
1248    /// [`SwanlingUser`](./swanling/struct.SwanlingUser.html)s
1249    fn allocate_task_sets(&mut self) -> Vec<usize> {
1250        trace!("allocate_task_sets");
1251
1252        let mut u: usize = 0;
1253        let mut v: usize;
1254        for task_set in &self.task_sets {
1255            if u == 0 {
1256                u = task_set.weight;
1257            } else {
1258                v = task_set.weight;
1259                trace!("calculating greatest common denominator of {} and {}", u, v);
1260                u = util::gcd(u, v);
1261                trace!("inner gcd: {}", u);
1262            }
1263        }
1264        // 'u' will always be the greatest common divisor
1265        debug!("gcd: {}", u);
1266
1267        // Build a vector of vectors to be used to schedule users.
1268        let mut available_task_sets = Vec::with_capacity(self.task_sets.len());
1269        let mut total_task_sets = 0;
1270        for (index, task_set) in self.task_sets.iter().enumerate() {
1271            // divide by greatest common divisor so vector is as short as possible
1272            let weight = task_set.weight / u;
1273            trace!(
1274                "{}: {} has weight of {} (reduced with gcd to {})",
1275                index,
1276                task_set.name,
1277                task_set.weight,
1278                weight
1279            );
1280            let weighted_sets = vec![index; weight];
1281            total_task_sets += weight;
1282            available_task_sets.push(weighted_sets);
1283        }
1284
1285        info!(
1286            "allocating tasks and task sets with {:?} scheduler",
1287            self.scheduler
1288        );
1289
1290        // Now build the weighted list with the appropriate scheduler.
1291        let mut weighted_task_sets = Vec::new();
1292        match self.scheduler {
1293            SwanlingScheduler::RoundRobin => {
1294                // Allocate task sets round robin.
1295                let task_sets_len = available_task_sets.len();
1296                loop {
1297                    for (task_set_index, task_sets) in available_task_sets
1298                        .iter_mut()
1299                        .enumerate()
1300                        .take(task_sets_len)
1301                    {
1302                        if let Some(task_set) = task_sets.pop() {
1303                            debug!("allocating 1 user from TaskSet {}", task_set_index);
1304                            weighted_task_sets.push(task_set);
1305                        }
1306                    }
1307                    if weighted_task_sets.len() >= total_task_sets {
1308                        break;
1309                    }
1310                }
1311            }
1312            SwanlingScheduler::Serial => {
1313                // Allocate task sets serially in the weighted order defined.
1314                for (task_set_index, task_sets) in available_task_sets.iter().enumerate() {
1315                    debug!(
1316                        "allocating all {} users from TaskSet {}",
1317                        task_sets.len(),
1318                        task_set_index
1319                    );
1320                    weighted_task_sets.append(&mut task_sets.clone());
1321                }
1322            }
1323            SwanlingScheduler::Random => {
1324                // Allocate task sets randomly.
1325                loop {
1326                    let task_set = available_task_sets.choose_mut(&mut rand::thread_rng());
1327                    match task_set {
1328                        Some(set) => {
1329                            if let Some(s) = set.pop() {
1330                                weighted_task_sets.push(s);
1331                            }
1332                        }
1333                        None => warn!("randomly allocating a SwanlingTaskSet failed, trying again"),
1334                    }
1335                    if weighted_task_sets.len() >= total_task_sets {
1336                        break;
1337                    }
1338                }
1339            }
1340        }
1341        weighted_task_sets
1342    }
1343
1344    /// Allocate a vector of weighted [`SwanlingUser`](./swanling/struct.SwanlingUser.html)s.
1345    fn weight_task_set_users(&mut self) -> Result<Vec<SwanlingUser>, SwanlingError> {
1346        trace!("weight_task_set_users");
1347
1348        let weighted_task_sets = self.allocate_task_sets();
1349
1350        // Allocate a state for each user that will be hatched.
1351        info!("initializing user states...");
1352        let mut weighted_users = Vec::new();
1353        let mut user_count = 0;
1354        loop {
1355            for task_sets_index in &weighted_task_sets {
1356                debug!(
1357                    "creating user state: {} ({})",
1358                    weighted_users.len(),
1359                    task_sets_index
1360                );
1361                let base_url = swanling::get_base_url(
1362                    self.get_configuration_host(),
1363                    self.task_sets[*task_sets_index].host.clone(),
1364                    self.defaults.host.clone(),
1365                )?;
1366                weighted_users.push(SwanlingUser::new(
1367                    self.task_sets[*task_sets_index].task_sets_index,
1368                    base_url,
1369                    self.task_sets[*task_sets_index].min_wait,
1370                    self.task_sets[*task_sets_index].max_wait,
1371                    &self.configuration,
1372                    self.metrics.hash,
1373                )?);
1374                user_count += 1;
1375                // Users are required here so unwrap() is safe.
1376                if user_count >= self.configuration.users.unwrap() {
1377                    debug!("created {} weighted_users", user_count);
1378                    return Ok(weighted_users);
1379                }
1380            }
1381        }
1382    }
1383
1384    /// Allocate a vector of weighted [`GaggleUser`](./swanling/struct.GaggleUser.html).
1385    fn prepare_worker_task_set_users(&mut self) -> Result<Vec<GaggleUser>, SwanlingError> {
1386        trace!("prepare_worker_task_set_users");
1387
1388        let weighted_task_sets = self.allocate_task_sets();
1389
1390        // Determine the users sent to each Worker.
1391        info!("preparing users for Workers...");
1392        let mut weighted_users = Vec::new();
1393        let mut user_count = 0;
1394        loop {
1395            for task_sets_index in &weighted_task_sets {
1396                let base_url = swanling::get_base_url(
1397                    self.get_configuration_host(),
1398                    self.task_sets[*task_sets_index].host.clone(),
1399                    self.defaults.host.clone(),
1400                )?;
1401                weighted_users.push(GaggleUser::new(
1402                    self.task_sets[*task_sets_index].task_sets_index,
1403                    base_url,
1404                    self.task_sets[*task_sets_index].min_wait,
1405                    self.task_sets[*task_sets_index].max_wait,
1406                    &self.configuration,
1407                    self.metrics.hash,
1408                ));
1409                user_count += 1;
1410                // Users are required here so unwrap() is safe.
1411                if user_count >= self.configuration.users.unwrap() {
1412                    debug!("prepared {} weighted_gaggle_users", user_count);
1413                    return Ok(weighted_users);
1414                }
1415            }
1416        }
1417    }
1418
1419    // Configure which mode this [`SwanlingAttack`](./struct.SwanlingAttack.html)
1420    // will run in.
1421    fn set_attack_mode(&mut self) -> Result<(), SwanlingError> {
1422        // Determine if Manager is enabled by default.
1423        let manager_is_default = if let Some(value) = self.defaults.manager {
1424            value
1425        } else {
1426            false
1427        };
1428
1429        // Determine if Worker is enabled by default.
1430        let worker_is_default = if let Some(value) = self.defaults.worker {
1431            value
1432        } else {
1433            false
1434        };
1435
1436        // Don't allow Manager and Worker to both be the default.
1437        if manager_is_default && worker_is_default {
1438            return Err(SwanlingError::InvalidOption {
1439                option: "SwanlingDefault::Worker".to_string(),
1440                value: "true".to_string(),
1441                detail: "The SwanlingDefault::Worker default can not be set together with the SwanlingDefault::Manager default"
1442                    .to_string(),
1443            });
1444        }
1445
1446        // Manager mode if --manager is set, or --worker is not set and Manager is default.
1447        if self.configuration.manager || (!self.configuration.worker && manager_is_default) {
1448            self.attack_mode = AttackMode::Manager;
1449            if self.configuration.worker {
1450                return Err(SwanlingError::InvalidOption {
1451                    option: "--worker".to_string(),
1452                    value: "true".to_string(),
1453                    detail: "The --worker flag can not be set together with the --manager flag"
1454                        .to_string(),
1455                });
1456            }
1457
1458            if !self.configuration.debug_log.is_empty() {
1459                return Err(SwanlingError::InvalidOption {
1460                    option: "--debug-file".to_string(),
1461                    value: self.configuration.debug_log.clone(),
1462                    detail:
1463                        "The --debug-file option can not be set together with the --manager flag."
1464                            .to_string(),
1465                });
1466            }
1467        }
1468
1469        // Worker mode if --worker is set, or --manager is not set and Worker is default.
1470        if self.configuration.worker || (!self.configuration.manager && worker_is_default) {
1471            self.attack_mode = AttackMode::Worker;
1472            if self.configuration.manager {
1473                return Err(SwanlingError::InvalidOption {
1474                    option: "--manager".to_string(),
1475                    value: "true".to_string(),
1476                    detail: "The --manager flag can not be set together with the --worker flag."
1477                        .to_string(),
1478                });
1479            }
1480
1481            if !self.configuration.host.is_empty() {
1482                return Err(SwanlingError::InvalidOption {
1483                    option: "--host".to_string(),
1484                    value: self.configuration.host.clone(),
1485                    detail: "The --host option can not be set together with the --worker flag."
1486                        .to_string(),
1487                });
1488            }
1489        }
1490
1491        // Otherwise run in standalone attack mode.
1492        if self.attack_mode == AttackMode::Undefined {
1493            self.attack_mode = AttackMode::StandAlone;
1494
1495            if self.configuration.no_hash_check {
1496                return Err(SwanlingError::InvalidOption {
1497                    option: "--no-hash-check".to_string(),
1498                    value: self.configuration.no_hash_check.to_string(),
1499                    detail: "The --no-hash-check flag can not be set without also setting the --manager flag.".to_string(),
1500                });
1501            }
1502        }
1503
1504        Ok(())
1505    }
1506
1507    // Change from one attack_phase to another.
1508    fn set_attack_phase(
1509        &mut self,
1510        swanling_attack_run_state: &mut SwanlingAttackRunState,
1511        phase: AttackPhase,
1512    ) {
1513        // There's nothing to do if already in the specified phase.
1514        if self.attack_phase == phase {
1515            return;
1516        }
1517
1518        // The drift timer starts at 0 any time the phase is changed.
1519        swanling_attack_run_state.drift_timer = tokio::time::Instant::now();
1520
1521        // Optional debug output.
1522        info!("entering SwanlingAttack phase: {:?}", &phase);
1523
1524        // Update the current phase.
1525        self.attack_phase = phase;
1526    }
1527
1528    // Determine how many Workers to expect.
1529    fn set_expect_workers(&mut self) -> Result<(), SwanlingError> {
1530        // Track how value gets set so we can return a meaningful error if necessary.
1531        let mut key = "configuration.expect_workers";
1532
1533        // Check if --expect-workers was set.
1534        if self.configuration.expect_workers.is_some() {
1535            key = "--expect-workers";
1536        // Otherwise check if a custom default is set.
1537        } else if let Some(default_expect_workers) = self.defaults.expect_workers {
1538            if self.attack_mode == AttackMode::Manager {
1539                key = "set_default(SwanlingDefault::ExpectWorkers)";
1540
1541                self.configuration.expect_workers = Some(default_expect_workers);
1542            }
1543        }
1544
1545        if let Some(expect_workers) = self.configuration.expect_workers {
1546            // Disallow --expect-workers without --manager.
1547            if self.attack_mode != AttackMode::Manager {
1548                return Err(SwanlingError::InvalidOption {
1549                    option: key.to_string(),
1550                    value: expect_workers.to_string(),
1551                    detail: format!(
1552                        "{} can not be set without also setting the --manager flag.",
1553                        key
1554                    ),
1555                });
1556            } else {
1557                // Must expect at least 1 Worker when running as Manager.
1558                if expect_workers < 1 {
1559                    return Err(SwanlingError::InvalidOption {
1560                        option: key.to_string(),
1561                        value: expect_workers.to_string(),
1562                        detail: format!("{} must be set to at least 1.", key),
1563                    });
1564                }
1565
1566                // Must not expect more Workers than Users. Users are required at this point so
1567                // using unwrap() is safe.
1568                if expect_workers as usize > self.configuration.users.unwrap() {
1569                    return Err(SwanlingError::InvalidOption {
1570                        option: key.to_string(),
1571                        value: expect_workers.to_string(),
1572                        detail: format!(
1573                            "{} can not be set to a value larger than --users option.",
1574                            key
1575                        ),
1576                    });
1577                }
1578            }
1579        }
1580
1581        Ok(())
1582    }
1583
1584    // Configure the host and port the Manager listens on.
1585    fn set_gaggle_host_and_port(&mut self) -> Result<(), SwanlingError> {
1586        // Configure manager_bind_host and manager_bind_port.
1587        if self.attack_mode == AttackMode::Manager {
1588            // Use default if run-time option not set.
1589            if self.configuration.manager_bind_host.is_empty() {
1590                self.configuration.manager_bind_host =
1591                    if let Some(host) = self.defaults.manager_bind_host.clone() {
1592                        host
1593                    } else {
1594                        "0.0.0.0".to_string()
1595                    }
1596            }
1597
1598            // Use default if run-time option not set.
1599            if self.configuration.manager_bind_port == 0 {
1600                self.configuration.manager_bind_port =
1601                    if let Some(port) = self.defaults.manager_bind_port {
1602                        port
1603                    } else {
1604                        DEFAULT_PORT.to_string().parse().unwrap()
1605                    };
1606            }
1607        } else {
1608            if !self.configuration.manager_bind_host.is_empty() {
1609                return Err(SwanlingError::InvalidOption {
1610                    option: "--manager-bind-host".to_string(),
1611                    value: self.configuration.manager_bind_host.clone(),
1612                    detail: "The --manager-bind-host option can not be set together with the --worker flag.".to_string(),
1613                });
1614            }
1615
1616            if self.configuration.manager_bind_port != 0 {
1617                return Err(SwanlingError::InvalidOption {
1618                    option: "--manager-bind-port".to_string(),
1619                    value: self.configuration.manager_bind_port.to_string(),
1620                    detail: "The --manager-bind-port option can not be set together with the --worker flag.".to_string(),
1621                });
1622            }
1623        }
1624
1625        // Configure manager_host and manager_port.
1626        if self.attack_mode == AttackMode::Worker {
1627            // Use default if run-time option not set.
1628            if self.configuration.manager_host.is_empty() {
1629                self.configuration.manager_host =
1630                    if let Some(host) = self.defaults.manager_host.clone() {
1631                        host
1632                    } else {
1633                        "127.0.0.1".to_string()
1634                    }
1635            }
1636
1637            // Use default if run-time option not set.
1638            if self.configuration.manager_port == 0 {
1639                self.configuration.manager_port = if let Some(port) = self.defaults.manager_port {
1640                    port
1641                } else {
1642                    DEFAULT_PORT.to_string().parse().unwrap()
1643                };
1644            }
1645        } else {
1646            if !self.configuration.manager_host.is_empty() {
1647                return Err(SwanlingError::InvalidOption {
1648                    option: "--manager-host".to_string(),
1649                    value: self.configuration.manager_host.clone(),
1650                    detail:
1651                        "The --manager-host option must be set together with the --worker flag."
1652                            .to_string(),
1653                });
1654            }
1655
1656            if self.configuration.manager_port != 0 {
1657                return Err(SwanlingError::InvalidOption {
1658                    option: "--manager-port".to_string(),
1659                    value: self.configuration.manager_port.to_string(),
1660                    detail:
1661                        "The --manager-port option must be set together with the --worker flag."
1662                            .to_string(),
1663                });
1664            }
1665        }
1666
1667        Ok(())
1668    }
1669
1670    // Configure how many [`SwanlingUser`](./swanling/struct.SwanlingUser.html)s to hatch.
1671    fn set_users(&mut self) -> Result<(), SwanlingError> {
1672        // Track how value gets set so we can return a meaningful error if necessary.
1673        let mut key = "configuration.users";
1674        let mut value = 0;
1675
1676        // Check if --users is set.
1677        if let Some(users) = self.configuration.users {
1678            key = "--users";
1679            value = users;
1680        // If not, check if a default for users is set.
1681        } else if let Some(default_users) = self.defaults.users {
1682            // On Worker users comes from the Manager.
1683            if self.attack_mode == AttackMode::Worker {
1684                self.configuration.users = None;
1685            // Otherwise use default.
1686            } else {
1687                key = "set_default(SwanlingDefault::Users)";
1688                value = default_users;
1689
1690                self.configuration.users = Some(default_users);
1691            }
1692        // If not and if not running on Worker, default to 1.
1693        } else if self.attack_mode != AttackMode::Worker {
1694            // This should not be able to fail, but setting up debug in case the number
1695            // of cpus library returns an invalid number.
1696            key = "num_cpus::get()";
1697            value = num_cpus::get();
1698
1699            info!("concurrent users defaulted to {} (number of CPUs)", value);
1700
1701            self.configuration.users = Some(value);
1702        }
1703
1704        // Perform bounds checking.
1705        if let Some(users) = self.configuration.users {
1706            // Setting --users with --worker is not allowed.
1707            if self.attack_mode == AttackMode::Worker {
1708                return Err(SwanlingError::InvalidOption {
1709                    option: key.to_string(),
1710                    value: value.to_string(),
1711                    detail: format!("{} can not be set together with the --worker flag.", key),
1712                });
1713            }
1714
1715            // Setting users to 0 is not allowed.
1716            if users == 0 {
1717                return Err(SwanlingError::InvalidOption {
1718                    option: key.to_string(),
1719                    value: "0".to_string(),
1720                    detail: "The --users option must be set to at least 1.".to_string(),
1721                });
1722            }
1723
1724            // Debug output.
1725            info!("users = {}", users);
1726        }
1727
1728        Ok(())
1729    }
1730
1731    // Configure maximum run time if specified, otherwise run until canceled.
1732    fn set_run_time(&mut self) -> Result<(), SwanlingError> {
1733        // Track how value gets set so we can return a meaningful error if necessary.
1734        let mut key = "configuration.run_time";
1735        let mut value = 0;
1736
1737        // Use --run-time if set, don't allow on Worker.
1738        self.run_time = if !self.configuration.run_time.is_empty() {
1739            key = "--run-time";
1740            value = util::parse_timespan(&self.configuration.run_time);
1741            value
1742        // Otherwise, use default if set, but not on Worker.
1743        } else if let Some(default_run_time) = self.defaults.run_time {
1744            if self.attack_mode == AttackMode::Worker {
1745                0
1746            } else {
1747                key = "set_default(SwanlingDefault::RunTime)";
1748                value = default_run_time;
1749                default_run_time
1750            }
1751        }
1752        // Otherwise the test runs until canceled.
1753        else {
1754            0
1755        };
1756
1757        if self.run_time > 0 {
1758            if self.attack_mode == AttackMode::Worker {
1759                return Err(SwanlingError::InvalidOption {
1760                    option: key.to_string(),
1761                    value: value.to_string(),
1762                    detail: format!("{} can not be set together with the --worker flag.", key),
1763                });
1764            }
1765
1766            // Debug output.
1767            info!("run_time = {}", self.run_time);
1768        }
1769
1770        Ok(())
1771    }
1772
1773    // Configure how quickly to hatch [`SwanlingUser`](./swanling/struct.SwanlingUser.html)s.
1774    fn set_hatch_rate(&mut self) -> Result<(), SwanlingError> {
1775        // Track how value gets set so we can return a meaningful error if necessary.
1776        let mut key = "configuration.hatch_rate";
1777        let mut value = "".to_string();
1778
1779        // Check if --hash-rate is set.
1780        if let Some(hatch_rate) = &self.configuration.hatch_rate {
1781            key = "--hatch_rate";
1782            value = hatch_rate.to_string();
1783        // If not, check if a default hatch_rate is set.
1784        } else if let Some(default_hatch_rate) = &self.defaults.hatch_rate {
1785            // On Worker hatch_rate comes from the Manager.
1786            if self.attack_mode == AttackMode::Worker {
1787                self.configuration.hatch_rate = None;
1788            // Otherwise use default.
1789            } else {
1790                key = "set_default(SwanlingDefault::HatchRate)";
1791                value = default_hatch_rate.to_string();
1792                self.configuration.hatch_rate = Some(default_hatch_rate.to_string());
1793            }
1794        // If not and if not running on Worker, default to 1.
1795        } else if self.attack_mode != AttackMode::Worker {
1796            // This should not be able to fail, but setting up debug in case a later
1797            // change introduces the potential for failure.
1798            key = "Swanling default";
1799            value = "1".to_string();
1800            self.configuration.hatch_rate = Some(value.to_string());
1801        }
1802
1803        // Verbose output.
1804        if let Some(hatch_rate) = &self.configuration.hatch_rate {
1805            // Setting --hatch-rate with --worker is not allowed.
1806            if self.attack_mode == AttackMode::Worker {
1807                return Err(SwanlingError::InvalidOption {
1808                    option: key.to_string(),
1809                    value,
1810                    detail: format!("{} can not be set together with the --worker flag.", key),
1811                });
1812            }
1813
1814            // Setting --hatch-rate of 0 is not allowed.
1815            if hatch_rate.is_empty() {
1816                return Err(SwanlingError::InvalidOption {
1817                    option: key.to_string(),
1818                    value,
1819                    detail: format!("{} must be set to at least 1.", key),
1820                });
1821            }
1822
1823            // Debug output.
1824            info!("hatch_rate = {}", hatch_rate);
1825        }
1826
1827        Ok(())
1828    }
1829
1830    // Configure the coordinated omission mitigation strategy.
1831    fn set_coordinated_omission(&mut self) -> Result<(), SwanlingError> {
1832        // Track how value gets set so we can return a meaningful error if necessary.
1833        let mut key = "configuration.coordinated_omission";
1834        let mut value = Some(SwanlingCoordinatedOmissionMitigation::Disabled);
1835
1836        if self.configuration.co_mitigation.is_some() {
1837            key = "--co-mitigation";
1838            value = self.configuration.co_mitigation.clone();
1839        }
1840
1841        // Use default for co_mitigation if set and not on Worker.
1842        if self.configuration.co_mitigation.is_none() {
1843            if let Some(default_co_mitigation) = self.defaults.co_mitigation.as_ref() {
1844                // In Gaggles, co_mitigation is only set on Manager.
1845                if self.attack_mode != AttackMode::Worker {
1846                    key = "set_default(SwanlingDefault::CoordinatedOmissionMitigation)";
1847                    value = Some(default_co_mitigation.clone());
1848
1849                    self.configuration.co_mitigation = Some(default_co_mitigation.clone());
1850                }
1851            }
1852        }
1853
1854        // Otherwise default to SwanlingCoordinaatedOmissionMitigation::Average.
1855        if self.configuration.co_mitigation.is_none() && self.attack_mode != AttackMode::Worker {
1856            self.configuration.co_mitigation = value.clone();
1857        }
1858
1859        if let Some(co_mitigation) = self.configuration.co_mitigation.as_ref() {
1860            // Setting --co-mitigation with --worker is not allowed.
1861            if self.attack_mode == AttackMode::Worker {
1862                return Err(SwanlingError::InvalidOption {
1863                    option: key.to_string(),
1864                    value: format!("{:?}", value),
1865                    detail: format!("{} can not be set together with the --worker flag.", key),
1866                });
1867            }
1868
1869            // Setting --co-mitigation with --no-metrics is not allowed.
1870            if self.configuration.no_metrics {
1871                return Err(SwanlingError::InvalidOption {
1872                    option: key.to_string(),
1873                    value: format!("{:?}", value),
1874                    detail: format!(
1875                        "{} can not be set together with the --no-metrics flag.",
1876                        key
1877                    ),
1878                });
1879            }
1880
1881            if co_mitigation != &SwanlingCoordinatedOmissionMitigation::Disabled
1882                && self.scheduler == SwanlingScheduler::Random
1883            {
1884                // Coordinated Omission Mitigation is not possible together with the random scheduler,
1885                // as it's impossible to calculate an accurate request cadence.
1886                return Err(SwanlingError::InvalidOption {
1887                    option: key.to_string(),
1888                    value: format!("{:?}", value),
1889                    detail: format!(
1890                        "{} can not be set together with SwanlingScheduler::Random.",
1891                        key
1892                    ),
1893                });
1894            }
1895
1896            info!(
1897                "co_mitigation = {:?}",
1898                self.configuration.co_mitigation.as_ref().unwrap()
1899            );
1900        }
1901
1902        Ok(())
1903    }
1904
1905    // Configure maximum requests per second if throttle enabled.
1906    fn set_throttle_requests(&mut self) -> Result<(), SwanlingError> {
1907        // Track how value gets set so we can return a meaningful error if necessary.
1908        let mut key = "configuration.throttle_requests";
1909        let mut value = 0;
1910
1911        if self.configuration.throttle_requests > 0 {
1912            key = "--throttle-requests";
1913            value = self.configuration.throttle_requests;
1914        }
1915
1916        // Use default for throttle_requests if set and not on Worker.
1917        if self.configuration.throttle_requests == 0 {
1918            if let Some(default_throttle_requests) = self.defaults.throttle_requests {
1919                // In Gaggles, throttle_requests is only set on Worker.
1920                if self.attack_mode != AttackMode::Manager {
1921                    key = "set_default(SwanlingDefault::ThrottleRequests)";
1922                    value = default_throttle_requests;
1923
1924                    self.configuration.throttle_requests = default_throttle_requests;
1925                }
1926            }
1927        }
1928
1929        if self.configuration.throttle_requests > 0 {
1930            // Setting --throttle-requests with --worker is not allowed.
1931            if self.attack_mode == AttackMode::Manager {
1932                return Err(SwanlingError::InvalidOption {
1933                    option: key.to_string(),
1934                    value: value.to_string(),
1935                    detail: format!("{} can not be set together with the --manager flag.", key),
1936                });
1937            }
1938
1939            // Be sure throttle_requests is in allowed range.
1940            if self.configuration.throttle_requests == 0 {
1941                return Err(SwanlingError::InvalidOption {
1942                    option: key.to_string(),
1943                    value: value.to_string(),
1944                    detail: format!("{} must be set to at least 1 request per second.", key),
1945                });
1946            } else if self.configuration.throttle_requests > 1_000_000 {
1947                return Err(SwanlingError::InvalidOption {
1948                    option: key.to_string(),
1949                    value: value.to_string(),
1950                    detail: format!(
1951                        "{} can not be set to more than 1,000,000 requests per second.",
1952                        key
1953                    ),
1954                });
1955            }
1956
1957            info!(
1958                "throttle_requests = {}",
1959                self.configuration.throttle_requests
1960            );
1961        }
1962
1963        Ok(())
1964    }
1965
1966    // Determine if `no_reset_statics` is enabled.
1967    fn set_no_reset_metrics(&mut self) -> Result<(), SwanlingError> {
1968        // Track how value gets set so we can return a meaningful error if necessary.
1969        let mut key = "configuration.no_reset_metrics";
1970        let mut value = false;
1971
1972        if self.configuration.no_reset_metrics {
1973            key = "--no-reset-metrics";
1974            value = true;
1975        // If not otherwise set and not Worker, check if there's a default.
1976        } else if self.attack_mode != AttackMode::Worker {
1977            if let Some(default_no_reset_metrics) = self.defaults.no_reset_metrics {
1978                key = "set_default(SwanlingDefault::NoResetMetrics)";
1979                value = default_no_reset_metrics;
1980
1981                // Optionally set default.
1982                self.configuration.no_reset_metrics = default_no_reset_metrics;
1983            }
1984        }
1985
1986        // Setting --no-reset-metrics with --worker is not allowed.
1987        if self.configuration.no_reset_metrics && self.attack_mode == AttackMode::Worker {
1988            return Err(SwanlingError::InvalidOption {
1989                option: key.to_string(),
1990                value: value.to_string(),
1991                detail: format!("{} can not be set together with the --worker flag.", key),
1992            });
1993        }
1994
1995        Ok(())
1996    }
1997
1998    // Determine if the `--status-codes` flag is enabled.
1999    fn set_status_codes(&mut self) -> Result<(), SwanlingError> {
2000        // Track how value gets set so we can return a meaningful error if necessary.
2001        let mut key = "configuration.status_codes";
2002        let mut value = false;
2003
2004        if self.configuration.status_codes {
2005            key = "--status-codes";
2006            value = true;
2007        // If not otherwise set and not Worker, check if there's a default.
2008        } else if self.attack_mode != AttackMode::Worker {
2009            if let Some(default_status_codes) = self.defaults.status_codes {
2010                key = "set_default(SwanlingDefault::StatusCodes)";
2011                value = default_status_codes;
2012
2013                // Optionally set default.
2014                self.configuration.status_codes = default_status_codes;
2015            }
2016        }
2017
2018        // Setting --status-codes with --worker is not allowed.
2019        if self.configuration.status_codes && self.attack_mode == AttackMode::Worker {
2020            return Err(SwanlingError::InvalidOption {
2021                option: key.to_string(),
2022                value: value.to_string(),
2023                detail: format!("{} can not be set together with the --worker flag.", key),
2024            });
2025        }
2026
2027        Ok(())
2028    }
2029
2030    // Determine if the `--running-metrics` flag is enabled.
2031    fn set_running_metrics(&mut self) -> Result<(), SwanlingError> {
2032        // Track how value gets set so we can return a meaningful error if necessary.
2033        let mut key = "configuration.running_metrics";
2034        let mut value = 0;
2035
2036        if let Some(running_metrics) = self.configuration.running_metrics {
2037            key = "--running-metrics";
2038            value = running_metrics;
2039        // If not otherwise set and not Worker, check if there's a default.
2040        } else if self.attack_mode != AttackMode::Worker {
2041            // Optionally set default.
2042            if let Some(default_running_metrics) = self.defaults.running_metrics {
2043                key = "set_default(SwanlingDefault::RunningMetrics)";
2044                value = default_running_metrics;
2045
2046                self.configuration.running_metrics = Some(default_running_metrics);
2047            }
2048        }
2049
2050        // Setting --running-metrics with --worker is not allowed.
2051        if let Some(running_metrics) = self.configuration.running_metrics {
2052            if self.attack_mode == AttackMode::Worker {
2053                return Err(SwanlingError::InvalidOption {
2054                    option: key.to_string(),
2055                    value: value.to_string(),
2056                    detail: format!("{} can not be set together with the --worker flag.", key),
2057                });
2058            }
2059
2060            if running_metrics > 0 {
2061                info!("running_metrics = {}", running_metrics);
2062            }
2063        }
2064
2065        Ok(())
2066    }
2067
2068    // Determine if the `--no-task-metrics` flag is enabled.
2069    fn set_no_task_metrics(&mut self) -> Result<(), SwanlingError> {
2070        // Track how value gets set so we can return a meaningful error if necessary.
2071        let mut key = "configuration.no_task_metrics";
2072        let mut value = false;
2073
2074        if self.configuration.no_task_metrics {
2075            key = "--no-task-metrics";
2076            value = true;
2077        // If not otherwise set and not Worker, check if there's a default.
2078        } else if self.attack_mode != AttackMode::Worker {
2079            // Optionally set default.
2080            if let Some(default_no_task_metrics) = self.defaults.no_task_metrics {
2081                key = "set_default(SwanlingDefault::NoTaskMetrics)";
2082                value = default_no_task_metrics;
2083
2084                self.configuration.no_task_metrics = default_no_task_metrics;
2085            }
2086        }
2087
2088        // Setting --no-task-metrics with --worker is not allowed.
2089        if self.configuration.no_task_metrics && self.attack_mode == AttackMode::Worker {
2090            return Err(SwanlingError::InvalidOption {
2091                option: key.to_string(),
2092                value: value.to_string(),
2093                detail: format!("{} can not be set together with the --worker flag.", key),
2094            });
2095        }
2096
2097        Ok(())
2098    }
2099
2100    // Determine if the `--no-error-summary` flag is enabled.
2101    fn set_no_error_summary(&mut self) -> Result<(), SwanlingError> {
2102        // Track how value gets set so we can return a meaningful error if necessary.
2103        let mut key = "configuration.no_error_summary";
2104        let mut value = false;
2105
2106        if self.configuration.no_error_summary {
2107            key = "--no-error-summary";
2108            value = true;
2109        // If not otherwise set and not Worker, check if there's a default.
2110        } else if self.attack_mode != AttackMode::Worker {
2111            // Optionally set default.
2112            if let Some(default_no_error_summary) = self.defaults.no_error_summary {
2113                key = "set_default(SwanlingDefault::NoErrorSummary)";
2114                value = default_no_error_summary;
2115
2116                self.configuration.no_error_summary = default_no_error_summary;
2117            }
2118        }
2119
2120        // Setting --no-error-summary with --worker is not allowed.
2121        if self.configuration.no_error_summary && self.attack_mode == AttackMode::Worker {
2122            return Err(SwanlingError::InvalidOption {
2123                option: key.to_string(),
2124                value: value.to_string(),
2125                detail: format!("{} can not be set together with the --worker flag.", key),
2126            });
2127        }
2128
2129        Ok(())
2130    }
2131
2132    // Determine if the `--no-metrics` flag is enabled.
2133    fn set_no_metrics(&mut self) -> Result<(), SwanlingError> {
2134        // Track how value gets set so we can return a meaningful error if necessary.
2135        let mut key = "configuration.no_metrics";
2136        let mut value = false;
2137
2138        if self.configuration.no_metrics {
2139            key = "--no-metrics";
2140            value = true;
2141        // If not otherwise set and not Worker, check if there's a default.
2142        } else if self.attack_mode != AttackMode::Worker {
2143            // Optionally set default.
2144            if let Some(default_no_metrics) = self.defaults.no_metrics {
2145                key = "set_default(SwanlingDefault::NoMetrics)";
2146                value = default_no_metrics;
2147
2148                self.configuration.no_metrics = default_no_metrics;
2149            }
2150        }
2151
2152        // Setting --no-metrics with --worker is not allowed.
2153        if self.configuration.no_metrics && self.attack_mode == AttackMode::Worker {
2154            return Err(SwanlingError::InvalidOption {
2155                option: key.to_string(),
2156                value: value.to_string(),
2157                detail: format!("{} can not be set together with the --worker flag.", key),
2158            });
2159        }
2160
2161        // Don't allow overhead of collecting metrics unless we're printing them.
2162        if self.configuration.no_metrics {
2163            if self.configuration.status_codes {
2164                return Err(SwanlingError::InvalidOption {
2165                    option: key.to_string(),
2166                    value: value.to_string(),
2167                    detail: format!(
2168                        "{} can not be set together with the --status-codes flag.",
2169                        key
2170                    ),
2171                });
2172            }
2173
2174            // Don't allow overhead of collecting metrics unless we're printing them.
2175            if self.configuration.running_metrics.is_some() {
2176                return Err(SwanlingError::InvalidOption {
2177                    option: key.to_string(),
2178                    value: value.to_string(),
2179                    detail: format!(
2180                        "{} can not be set together with the --running_metrics option.",
2181                        key
2182                    ),
2183                });
2184            }
2185
2186            // There is nothing to log if metrics are disabled.
2187            if !self.configuration.request_log.is_empty() {
2188                return Err(SwanlingError::InvalidOption {
2189                    option: key.to_string(),
2190                    value: value.to_string(),
2191                    detail: format!(
2192                        "{} can not be set together with the --requests-file option.",
2193                        key
2194                    ),
2195                });
2196            }
2197        }
2198
2199        Ok(())
2200    }
2201
2202    // Determine if the `--sticky-follow` flag is enabled.
2203    fn set_sticky_follow(&mut self) -> Result<(), SwanlingError> {
2204        // Track how value gets set so we can return a meaningful error if necessary.
2205        let mut key = "configuration.sticky_follow";
2206        let mut value = false;
2207
2208        if self.configuration.sticky_follow {
2209            key = "--sticky-follow";
2210            value = true;
2211        // If not otherwise set and not Worker, check if there's a default.
2212        } else if self.attack_mode != AttackMode::Worker {
2213            // Optionally set default.
2214            if let Some(default_sticky_follow) = self.defaults.sticky_follow {
2215                key = "set_default(SwanlingDefault::StickyFollow)";
2216                value = default_sticky_follow;
2217
2218                self.configuration.sticky_follow = default_sticky_follow;
2219            }
2220        }
2221
2222        if self.configuration.sticky_follow && self.attack_mode == AttackMode::Worker {
2223            return Err(SwanlingError::InvalidOption {
2224                option: key.to_string(),
2225                value: value.to_string(),
2226                detail: format!("{} can not be set together with the --worker flag.", key),
2227            });
2228        }
2229
2230        Ok(())
2231    }
2232
2233    #[cfg(feature = "gaggle")]
2234    // Determine if `--no-hash-check` flag is enabled.
2235    fn set_no_hash_check(&mut self) -> Result<(), SwanlingError> {
2236        // Track how value gets set so we can return a meaningful error if necessary.
2237        let mut key = "configuration.no_hash_check";
2238        let mut value = false;
2239
2240        if self.configuration.no_hash_check {
2241            key = "--no-hash-check";
2242            value = true;
2243        // If not otherwise set and not Worker, check if there's a default.
2244        } else if self.attack_mode != AttackMode::Worker {
2245            // Optionally set default.
2246            if let Some(default_no_hash_check) = self.defaults.no_hash_check {
2247                key = "set_default(SwanlingDefault::NoHashCheck)";
2248                value = default_no_hash_check;
2249
2250                self.configuration.no_hash_check = default_no_hash_check;
2251            }
2252        }
2253
2254        if self.configuration.no_hash_check && self.attack_mode == AttackMode::Worker {
2255            return Err(SwanlingError::InvalidOption {
2256                option: key.to_string(),
2257                value: value.to_string(),
2258                detail: format!("{} can not be set together with the --worker flag.", key),
2259            });
2260        }
2261
2262        Ok(())
2263    }
2264
2265    // If enabled, returns the path of the report_file, otherwise returns None.
2266    fn get_report_file_path(&mut self) -> Option<String> {
2267        // If metrics are disabled, or running in Manager mode, there is no
2268        // report file, exit immediately.
2269        if self.configuration.no_metrics || self.attack_mode == AttackMode::Manager {
2270            return None;
2271        }
2272
2273        // If --report-file is set, return it.
2274        if !self.configuration.report_file.is_empty() {
2275            return Some(self.configuration.report_file.to_string());
2276        }
2277
2278        // If SwanlingDefault::ReportFile is set, return it.
2279        if let Some(default_report_file) = &self.defaults.report_file {
2280            return Some(default_report_file.to_string());
2281        }
2282
2283        // Otherwise there is no report file.
2284        None
2285    }
2286
2287    // Configure requests log format.
2288    fn set_request_format(&mut self) -> Result<(), SwanlingError> {
2289        // Track how value gets set so we can return a meaningful error if necessary.
2290        let mut key = "configuration.request_format";
2291        let mut value = Some(SwanlingLogFormat::Json);
2292
2293        if self.configuration.request_format.is_some() {
2294            key = "--requests-format";
2295            value = self.configuration.request_format.clone();
2296        } else if let Some(default_request_format) = self.defaults.request_format.as_ref() {
2297            // In Gaggles, request_format is only set on Worker.
2298            if self.attack_mode != AttackMode::Manager {
2299                key = "set_default(SwanlingDefault::RequestFormat)";
2300                value = Some(default_request_format.clone());
2301                self.configuration.request_format = Some(default_request_format.clone());
2302            }
2303        }
2304
2305        // Otherwise default to SwanlingLogFormat::Json.
2306        if !self.configuration.request_log.is_empty()
2307            && self.configuration.request_format.is_none()
2308            && self.attack_mode != AttackMode::Manager
2309        {
2310            self.configuration.request_format = value.clone();
2311        }
2312
2313        if self.configuration.request_format.is_some() {
2314            // Log format isn't relevant if metrics aren't enabled.
2315            if self.configuration.no_metrics {
2316                return Err(SwanlingError::InvalidOption {
2317                    option: "--no-metrics".to_string(),
2318                    value: "true".to_string(),
2319                    detail: "The --no-metrics flag can not be set together with the --requests-format option.".to_string(),
2320                });
2321            }
2322            // Log format isn't relevant if log not enabled.
2323            else if self.configuration.request_log.is_empty() {
2324                return Err(SwanlingError::InvalidOption {
2325                    option: key.to_string(),
2326                    value: format!("{:?}", value),
2327                    detail: "The --requests-file option must be set together with the --requests-format option.".to_string(),
2328                });
2329            }
2330        }
2331
2332        Ok(())
2333    }
2334
2335    // Configure tasks log format.
2336    fn set_task_format(&mut self) -> Result<(), SwanlingError> {
2337        // Track how value gets set so we can return a meaningful error if necessary.
2338        let mut key = "configuration.task_format";
2339        let mut value = Some(SwanlingLogFormat::Json);
2340
2341        if self.configuration.task_format.is_some() {
2342            key = "--tasks-format";
2343            value = self.configuration.task_format.clone();
2344        } else if let Some(default_task_format) = self.defaults.task_format.as_ref() {
2345            // In Gaggles, task_format is only set on Worker.
2346            if self.attack_mode != AttackMode::Manager {
2347                key = "set_default(SwanlingDefault::TaskFormat)";
2348                value = Some(default_task_format.clone());
2349                self.configuration.task_format = Some(default_task_format.clone());
2350            }
2351        }
2352
2353        // Otherwise default to SwanlingLogFormat::Json.
2354        if !self.configuration.task_log.is_empty()
2355            && self.configuration.task_format.is_none()
2356            && self.attack_mode != AttackMode::Manager
2357        {
2358            self.configuration.task_format = value.clone();
2359        }
2360
2361        if self.configuration.task_format.is_some() {
2362            // Log format isn't relevant if metrics aren't enabled.
2363            if self.configuration.no_metrics {
2364                return Err(SwanlingError::InvalidOption {
2365                    option: "--no-metrics".to_string(),
2366                    value: "true".to_string(),
2367                    detail: "The --no-metrics flag can not be set together with the --tasks-format option.".to_string(),
2368                });
2369            }
2370            // Log format isn't relevant if log not enabled.
2371            else if self.configuration.task_log.is_empty() {
2372                return Err(SwanlingError::InvalidOption {
2373                    option: key.to_string(),
2374                    value: format!("{:?}", value),
2375                    detail: "The --tasks-file option must be set together with the --tasks-format option.".to_string(),
2376                });
2377            }
2378        }
2379
2380        Ok(())
2381    }
2382
2383    // Configure tasks log format.
2384    fn set_error_format(&mut self) -> Result<(), SwanlingError> {
2385        // Track how value gets set so we can return a meaningful error if necessary.
2386        let mut key = "configuration.error_format";
2387        let mut value = Some(SwanlingLogFormat::Json);
2388
2389        if self.configuration.error_format.is_some() {
2390            key = "--error-format";
2391            value = self.configuration.error_format.clone();
2392        } else if let Some(default_error_format) = self.defaults.error_format.as_ref() {
2393            // In Gaggles, error_format is only set on Worker.
2394            if self.attack_mode != AttackMode::Manager {
2395                key = "set_default(SwanlingDefault::ErrorFormat)";
2396                value = Some(default_error_format.clone());
2397                self.configuration.error_format = Some(default_error_format.clone());
2398            }
2399        }
2400
2401        // Otherwise default to SwanlingLogFormat::Json.
2402        if !self.configuration.error_log.is_empty()
2403            && self.configuration.error_format.is_none()
2404            && self.attack_mode != AttackMode::Manager
2405        {
2406            self.configuration.error_format = value.clone();
2407        }
2408
2409        if self.configuration.error_format.is_some() {
2410            // Log format isn't relevant if metrics aren't enabled.
2411            if self.configuration.no_metrics {
2412                return Err(SwanlingError::InvalidOption {
2413                    option: "--no-metrics".to_string(),
2414                    value: "true".to_string(),
2415                    detail: "The --no-metrics flag can not be set together with the --error-format option.".to_string(),
2416                });
2417            }
2418            // Log format isn't relevant if log not enabled.
2419            else if self.configuration.error_log.is_empty() {
2420                return Err(SwanlingError::InvalidOption {
2421                    option: key.to_string(),
2422                    value: format!("{:?}", value),
2423                    detail: "The --error-file option must be set together with the --error-format option.".to_string(),
2424                });
2425            }
2426        }
2427
2428        Ok(())
2429    }
2430
2431    // Configure debug log format.
2432    fn set_debug_format(&mut self) -> Result<(), SwanlingError> {
2433        // Track how value gets set so we can return a meaningful error if necessary.
2434        let mut key = "configuration.debug_format";
2435        let mut value = Some(SwanlingLogFormat::Json);
2436
2437        if self.configuration.debug_format.is_some() {
2438            key = "--debug-format";
2439            value = self.configuration.debug_format.clone();
2440        } else if let Some(default_debug_format) = self.defaults.debug_format.as_ref() {
2441            // In Gaggles, debug_format is only set on Worker.
2442            if self.attack_mode != AttackMode::Manager {
2443                key = "set_default(SwanlingDefault::DebugFormat)";
2444                value = Some(default_debug_format.clone());
2445                self.configuration.debug_format = Some(default_debug_format.clone());
2446            }
2447        }
2448
2449        // Otherwise default to SwanlingLogFormat::Json.
2450        if !self.configuration.debug_log.is_empty()
2451            && self.configuration.debug_format.is_none()
2452            && self.attack_mode != AttackMode::Manager
2453        {
2454            self.configuration.debug_format = value.clone();
2455        }
2456
2457        if self.configuration.debug_format.is_some() {
2458            // Log format isn't relevant if log not enabled.
2459            if self.configuration.debug_log.is_empty() {
2460                return Err(SwanlingError::InvalidOption {
2461                    option: key.to_string(),
2462                    value: format!("{:?}", value),
2463                    detail: "The --debug-file option must be set together with the --debug-format option.".to_string(),
2464                });
2465            }
2466        }
2467
2468        Ok(())
2469    }
2470
2471    // Configure whether or not to enable the telnet Controller. Always disable when in Regatta mode.
2472    fn set_no_telnet(&mut self) {
2473        // Currently Gaggles are not Controller-aware, force disable.
2474        if [AttackMode::Manager, AttackMode::Worker].contains(&self.attack_mode) {
2475            self.configuration.no_telnet = true;
2476        // Otherwise, if --no-telnet flag not set, respect default if configured.
2477        } else if !self.configuration.no_telnet {
2478            if let Some(default_no_telnet) = self.defaults.no_telnet {
2479                self.configuration.no_telnet = default_no_telnet;
2480            }
2481        }
2482    }
2483
2484    // Configure whether or not to enable the WebSocket Controller. Always disable when in Regatta mode.
2485    fn set_no_websocket(&mut self) {
2486        // Currently Gaggles are not Controller-aware, force disable.
2487        if [AttackMode::Manager, AttackMode::Worker].contains(&self.attack_mode) {
2488            self.configuration.no_websocket = true;
2489        // Otherwise, if --no-websocket flag not set, respect default if configured.
2490        } else if !self.configuration.no_websocket {
2491            if let Some(default_no_telnet) = self.defaults.no_telnet {
2492                self.configuration.no_websocket = default_no_telnet;
2493            }
2494        }
2495    }
2496
2497    // Configure whether or not to autostart the load test.
2498    fn set_no_autostart(&mut self) -> Result<(), SwanlingError> {
2499        // Track how value gets set so we can return a meaningful error if necessary.
2500        let mut key = "configuration.no_autostart";
2501        let mut value = false;
2502
2503        // Currently Gaggles are not Controller-aware.
2504        if self.configuration.no_autostart {
2505            key = "--no-autostart";
2506            value = true;
2507        // Otherwise set default if configured.
2508        } else if let Some(default_no_autostart) = self.defaults.no_autostart {
2509            key = "set_default(SwanlingDefault::NoAutoStart)";
2510            value = default_no_autostart;
2511
2512            self.configuration.no_autostart = default_no_autostart;
2513        }
2514
2515        if self.configuration.no_autostart {
2516            // Can't disable autostart in Regatta mode.
2517            if [AttackMode::Manager, AttackMode::Worker].contains(&self.attack_mode) {
2518                return Err(SwanlingError::InvalidOption {
2519                    option: key.to_string(),
2520                    value: value.to_string(),
2521                    detail: format!(
2522                        "{} can not be set together with the --manager or --worker flags.",
2523                        key
2524                    ),
2525                });
2526            }
2527
2528            // Can't disable autostart if there's no Controller enabled.
2529            if self.configuration.no_telnet && self.configuration.no_websocket {
2530                return Err(SwanlingError::InvalidOption {
2531                    option: key.to_string(),
2532                    value: value.to_string(),
2533                    detail: format!("{} can not be set together with both the --no-telnet and --no-websocket flags.", key),
2534                });
2535            }
2536        }
2537
2538        Ok(())
2539    }
2540
2541    // Configure whether to log response body.
2542    fn set_no_debug_body(&mut self) -> Result<(), SwanlingError> {
2543        // Track how value gets set so we can return a meaningful error if necessary.
2544        let mut key = "configuration.no_debug_body";
2545        let mut value = false;
2546
2547        if self.configuration.no_debug_body {
2548            key = "--no-debug-body";
2549            value = true;
2550        // If not otherwise set and not Manager, check if there's a default.
2551        } else if self.attack_mode != AttackMode::Manager {
2552            // Optionally set default.
2553            if let Some(default_no_debug_body) = self.defaults.no_debug_body {
2554                key = "set_default(SwanlingDefault::NoDebugBody)";
2555                value = default_no_debug_body;
2556
2557                self.configuration.no_debug_body = default_no_debug_body;
2558            }
2559        }
2560
2561        if self.configuration.no_debug_body && self.attack_mode == AttackMode::Manager {
2562            return Err(SwanlingError::InvalidOption {
2563                option: key.to_string(),
2564                value: value.to_string(),
2565                detail: format!("{} can not be set together with the --manager flag.", key),
2566            });
2567        }
2568
2569        Ok(())
2570    }
2571
2572    /// Execute the [`SwanlingAttack`](./struct.SwanlingAttack.html) load test.
2573    ///
2574    /// # Example
2575    /// ```rust
2576    /// use swanling::prelude::*;
2577    ///
2578    /// fn main() -> Result<(), SwanlingError> {
2579    ///     let _swanling_metrics = SwanlingAttack::initialize()?
2580    ///         .register_taskset(taskset!("ExampleTasks")
2581    ///             .register_task(task!(example_task).set_weight(2)?)
2582    ///             .register_task(task!(another_example_task).set_weight(3)?)
2583    ///             // Swanling must run against a host, point to localhost so test starts.
2584    ///             .set_host("http://localhost")
2585    ///         )
2586    ///         // Exit after one second so test doesn't run forever.
2587    ///         .set_default(SwanlingDefault::RunTime, 1)?
2588    ///         .execute()?;
2589    ///
2590    ///     Ok(())
2591    /// }
2592    ///
2593    /// async fn example_task(user: &SwanlingUser) -> SwanlingTaskResult {
2594    ///     let _swanling = user.get("/foo").await?;
2595    ///
2596    ///     Ok(())
2597    /// }
2598    ///
2599    /// async fn another_example_task(user: &SwanlingUser) -> SwanlingTaskResult {
2600    ///     let _swanling = user.get("/bar").await?;
2601    ///
2602    ///     Ok(())
2603    /// }
2604    /// ```
2605    pub fn execute(mut self) -> Result<SwanlingMetrics, SwanlingError> {
2606        // If version flag is set, display package name and version and exit.
2607        if self.configuration.version {
2608            println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
2609            std::process::exit(0);
2610        }
2611
2612        // At least one task set is required.
2613        if self.task_sets.is_empty() {
2614            return Err(SwanlingError::NoTaskSets {
2615                detail: "No task sets are defined.".to_string(),
2616            });
2617        }
2618
2619        // Display task sets and tasks, then exit.
2620        if self.configuration.list {
2621            println!("Available tasks:");
2622            for task_set in self.task_sets {
2623                println!(" - {} (weight: {})", task_set.name, task_set.weight);
2624                for task in task_set.tasks {
2625                    println!("    o {} (weight: {})", task.name, task.weight);
2626                }
2627            }
2628            std::process::exit(0);
2629        }
2630
2631        // Configure loggers.
2632        self.configuration.configure_loggers(&self.defaults);
2633
2634        // Initialize logger.
2635        self.initialize_logger();
2636
2637        // Configure run mode (StandAlone, Worker, Manager).
2638        self.set_attack_mode()?;
2639
2640        // Determine whether or not to enable the telnet Controller.
2641        self.set_no_telnet();
2642
2643        // Determine whether or not to enable the WebSocket Controller.
2644        self.set_no_websocket();
2645
2646        // Determine whether or not to autostart load test.
2647        self.set_no_autostart()?;
2648
2649        // Configure number of users to simulate.
2650        self.set_users()?;
2651
2652        // Configure expect_workers if running in Manager attack mode.
2653        self.set_expect_workers()?;
2654
2655        // Configure host and ports if running in a Regatta distributed load test.
2656        self.set_gaggle_host_and_port()?;
2657
2658        // Configure how long to run.
2659        self.set_run_time()?;
2660
2661        // Configure how many users to hatch per second.
2662        self.set_hatch_rate()?;
2663
2664        // Configure the requests log format.
2665        self.set_request_format()?;
2666
2667        // Configure the tasks log format.
2668        self.set_task_format()?;
2669
2670        // Configure the tasks log format.
2671        self.set_error_format()?;
2672
2673        // Configure the debug log format.
2674        self.set_debug_format()?;
2675
2676        // Determine whether or not to log response body.
2677        self.set_no_debug_body()?;
2678
2679        // Configure coordinated ommission mitigation strategy.
2680        self.set_coordinated_omission()?;
2681
2682        // Configure throttle if enabled.
2683        self.set_throttle_requests()?;
2684
2685        // Configure status_codes flag.
2686        self.set_status_codes()?;
2687
2688        // Configure running_metrics flag.
2689        self.set_running_metrics()?;
2690
2691        // Configure no_reset_metrics flag.
2692        self.set_no_reset_metrics()?;
2693
2694        // Configure no_task_metrics flag.
2695        self.set_no_task_metrics()?;
2696
2697        // Configure no_error_summary flag.
2698        self.set_no_error_summary()?;
2699
2700        // Configure no_metrics flag.
2701        self.set_no_metrics()?;
2702
2703        // Configure sticky_follow flag.
2704        self.set_sticky_follow()?;
2705
2706        // Configure no_hash_check flag.
2707        #[cfg(feature = "gaggle")]
2708        self.set_no_hash_check()?;
2709
2710        // Confirm there's either a global host, or each task set has a host defined.
2711        if let Err(e) = self.validate_host() {
2712            if self.configuration.no_autostart {
2713                info!("host must be configured via Controller before starting load test");
2714            } else {
2715                // If auto-starting, host must be valid.
2716                return Err(e);
2717            }
2718        } else {
2719            info!("global host configured: {}", self.configuration.host);
2720            self.prepare_load_test()?;
2721        }
2722
2723        // Calculate a unique hash for the current load test.
2724        let mut s = DefaultHasher::new();
2725        self.task_sets.hash(&mut s);
2726        self.metrics.hash = s.finish();
2727        debug!("hash: {}", self.metrics.hash);
2728
2729        // Start swanling in manager mode.
2730        if self.attack_mode == AttackMode::Manager {
2731            #[cfg(feature = "gaggle")]
2732            {
2733                let rt = Runtime::new().unwrap();
2734                self = rt.block_on(manager::manager_main(self));
2735            }
2736
2737            #[cfg(not(feature = "gaggle"))]
2738            {
2739                return Err(SwanlingError::FeatureNotEnabled {
2740                    feature: "gaggle".to_string(), detail: "Load test must be recompiled with `--features gaggle` to start in manager mode.".to_string()
2741                });
2742            }
2743        }
2744        // Start swanling in worker mode.
2745        else if self.attack_mode == AttackMode::Worker {
2746            #[cfg(feature = "gaggle")]
2747            {
2748                let rt = Runtime::new().unwrap();
2749                self = rt.block_on(worker::worker_main(&self));
2750            }
2751
2752            #[cfg(not(feature = "gaggle"))]
2753            {
2754                return Err(SwanlingError::FeatureNotEnabled {
2755                    feature: "gaggle".to_string(),
2756                    detail: "Load test must be recompiled with `--features gaggle` to start in worker mode.".to_string(),
2757                });
2758            }
2759        }
2760        // Start swanling in single-process mode.
2761        else {
2762            let rt = Runtime::new().unwrap();
2763            self = rt.block_on(self.start_attack(None))?;
2764        }
2765
2766        Ok(self.metrics)
2767    }
2768
2769    // Returns OK(()) if there's a valid host, SwanlingError with details if not.
2770    fn validate_host(&mut self) -> Result<(), SwanlingError> {
2771        if self.configuration.host.is_empty() {
2772            for task_set in &self.task_sets {
2773                match &task_set.host {
2774                    Some(h) => {
2775                        if util::is_valid_host(h).is_ok() {
2776                            info!("host for {} configured: {}", task_set.name, h);
2777                        }
2778                    }
2779                    None => match &self.defaults.host {
2780                        Some(h) => {
2781                            if util::is_valid_host(h).is_ok() {
2782                                info!("host for {} configured: {}", task_set.name, h);
2783                            }
2784                        }
2785                        None => {
2786                            if self.attack_mode != AttackMode::Worker {
2787                                return Err(SwanlingError::InvalidOption {
2788                                    option: "--host".to_string(),
2789                                    value: "".to_string(),
2790                                    detail: format!("A host must be defined via the --host option, the SwanlingAttack.set_default() function, or the SwanlingTaskSet.set_host() function (no host defined for {}).", task_set.name)
2791                                });
2792                            }
2793                        }
2794                    },
2795                }
2796            }
2797        }
2798        Ok(())
2799    }
2800
2801    // Create and schedule SwanlingUsers. This requires that the host that will be load tested
2802    // has been configured.
2803    fn prepare_load_test(&mut self) -> Result<(), SwanlingError> {
2804        // If not on a Worker, be sure a valid host has been defined before building configuration.
2805        if self.attack_mode != AttackMode::Worker {
2806            self.validate_host()?;
2807        }
2808
2809        // Apply weights to tasks in each task set.
2810        for task_set in &mut self.task_sets {
2811            let (weighted_on_start_tasks, weighted_tasks, weighted_on_stop_tasks) =
2812                allocate_tasks(&task_set, &self.scheduler);
2813            task_set.weighted_on_start_tasks = weighted_on_start_tasks;
2814            task_set.weighted_tasks = weighted_tasks;
2815            task_set.weighted_on_stop_tasks = weighted_on_stop_tasks;
2816            debug!(
2817                "weighted {} on_start: {:?} tasks: {:?} on_stop: {:?}",
2818                task_set.name,
2819                task_set.weighted_on_start_tasks,
2820                task_set.weighted_tasks,
2821                task_set.weighted_on_stop_tasks
2822            );
2823        }
2824
2825        if self.attack_mode != AttackMode::Worker {
2826            // Stand-alone and Manager processes can display metrics.
2827            if !self.configuration.no_metrics {
2828                self.metrics.display_metrics = true;
2829            }
2830
2831            if self.attack_mode == AttackMode::StandAlone {
2832                // Allocate a state for each of the users we are about to start.
2833                self.weighted_users = self.weight_task_set_users()?;
2834            } else if self.attack_mode == AttackMode::Manager {
2835                // Build a list of users to be allocated on Workers.
2836                self.weighted_gaggle_users = self.prepare_worker_task_set_users()?;
2837            }
2838        }
2839
2840        Ok(())
2841    }
2842
2843    /// Helper to wrap configured host in `Option<>` if set.
2844    fn get_configuration_host(&self) -> Option<String> {
2845        if self.configuration.host.is_empty() {
2846            None
2847        } else {
2848            Some(self.configuration.host.to_string())
2849        }
2850    }
2851
2852    // Helper to spawn a throttle thread if configured. The throttle thread opens
2853    // a bounded channel to control how quickly [`SwanlingUser`](./swanling/struct.SwanlingUser.html)
2854    // threads can make requests.
2855    async fn setup_throttle(
2856        &self,
2857    ) -> (
2858        // A channel used by [`SwanlingUser`](./swanling/struct.SwanlingUser.html)s to throttle requests.
2859        Option<flume::Sender<bool>>,
2860        // A channel used by parent to tell throttle the load test is complete.
2861        Option<flume::Sender<bool>>,
2862    ) {
2863        // If the throttle isn't enabled, return immediately.
2864        if self.configuration.throttle_requests == 0 {
2865            return (None, None);
2866        }
2867
2868        // Create a bounded channel allowing single-sender multi-receiver to throttle
2869        // [`SwanlingUser`](./swanling/struct.SwanlingUser.html) threads.
2870        let (all_threads_throttle, throttle_receiver): (
2871            flume::Sender<bool>,
2872            flume::Receiver<bool>,
2873        ) = flume::bounded(self.configuration.throttle_requests);
2874
2875        // Create a channel allowing the parent to inform the throttle thread when the
2876        // load test is finished. Even though we only send one message, we can't use a
2877        // oneshot channel as we don't want to block waiting for a message.
2878        let (parent_to_throttle_tx, throttle_rx) = flume::bounded(1);
2879
2880        // Launch a new thread for throttling, no need to rejoin it.
2881        let _ = Some(tokio::spawn(throttle::throttle_main(
2882            self.configuration.throttle_requests,
2883            throttle_receiver,
2884            throttle_rx,
2885        )));
2886
2887        let sender = all_threads_throttle.clone();
2888        // We start from 1 instead of 0 to intentionally fill all but one slot in the
2889        // channel to avoid a burst of traffic during startup. The channel then provides
2890        // an implementation of the leaky bucket algorithm as a queue. Requests have to
2891        // add a token to the bucket before making a request, and are blocked until this
2892        // throttle thread "leaks out" a token thereby creating space. More information
2893        // can be found at: https://en.wikipedia.org/wiki/Leaky_bucket
2894        for _ in 1..self.configuration.throttle_requests {
2895            let _ = sender.send_async(true).await;
2896        }
2897
2898        (Some(all_threads_throttle), Some(parent_to_throttle_tx))
2899    }
2900
2901    // Helper to optionally spawn a telnet and/or WebSocket Controller thread. The Controller
2902    // threads share a control channel, allowing it to send requests to the parent process. When
2903    // a response is required, the Controller will also send a one-shot channel allowing a direct
2904    // reply.
2905    async fn setup_controllers(&mut self) -> Option<flume::Receiver<SwanlingControllerRequest>> {
2906        // If the telnet controller is disabled, return immediately.
2907        if self.configuration.no_telnet && self.configuration.no_websocket {
2908            return None;
2909        }
2910
2911        // Create an unbounded channel for controller threads to send requests to the parent
2912        // process.
2913        let (all_threads_controller_request_tx, controller_request_rx): (
2914            flume::Sender<SwanlingControllerRequest>,
2915            flume::Receiver<SwanlingControllerRequest>,
2916        ) = flume::unbounded();
2917
2918        // Configured telnet Controller if not disabled.
2919        if !self.configuration.no_telnet {
2920            // Configure telnet_host, using default if run-time option is not set.
2921            if self.configuration.telnet_host.is_empty() {
2922                self.configuration.telnet_host =
2923                    if let Some(host) = self.defaults.telnet_host.clone() {
2924                        host
2925                    } else {
2926                        "0.0.0.0".to_string()
2927                    }
2928            }
2929
2930            // Then configure telnet_port, using default if run-time option is not set.
2931            if self.configuration.telnet_port == 0 {
2932                self.configuration.telnet_port = if let Some(port) = self.defaults.telnet_port {
2933                    port
2934                } else {
2935                    DEFAULT_TELNET_PORT.to_string().parse().unwrap()
2936                };
2937            }
2938
2939            // Spawn the initial controller thread to allow real-time control of the load test.
2940            // There is no need to rejoin this thread when the load test ends.
2941            let _ = Some(tokio::spawn(controller::controller_main(
2942                self.configuration.clone(),
2943                all_threads_controller_request_tx.clone(),
2944                SwanlingControllerProtocol::Telnet,
2945            )));
2946        }
2947
2948        // Configured WebSocket Controller if not disabled.
2949        if !self.configuration.no_websocket {
2950            // Configure websocket_host, using default if run-time option is not set.
2951            if self.configuration.websocket_host.is_empty() {
2952                self.configuration.websocket_host =
2953                    if let Some(host) = self.defaults.websocket_host.clone() {
2954                        host
2955                    } else {
2956                        "0.0.0.0".to_string()
2957                    }
2958            }
2959
2960            // Then configure websocket_port, using default if run-time option is not set.
2961            if self.configuration.websocket_port == 0 {
2962                self.configuration.websocket_port = if let Some(port) = self.defaults.websocket_port
2963                {
2964                    port
2965                } else {
2966                    DEFAULT_WEBSOCKET_PORT.to_string().parse().unwrap()
2967                };
2968            }
2969
2970            // Spawn the initial controller thread to allow real-time control of the load test.
2971            // There is no need to rejoin this thread when the load test ends.
2972            let _ = Some(tokio::spawn(controller::controller_main(
2973                self.configuration.clone(),
2974                all_threads_controller_request_tx,
2975                SwanlingControllerProtocol::WebSocket,
2976            )));
2977        }
2978
2979        // Return the parent end of the Controller channel.
2980        Some(controller_request_rx)
2981    }
2982
2983    // Prepare an asynchronous file writer for `report_file` (if enabled).
2984    async fn prepare_report_file(&mut self) -> Result<Option<File>, SwanlingError> {
2985        if let Some(report_file_path) = self.get_report_file_path() {
2986            Ok(Some(File::create(&report_file_path).await?))
2987        } else {
2988            Ok(None)
2989        }
2990    }
2991
2992    // Invoke `test_start` tasks if existing.
2993    async fn run_test_start(&self) -> Result<(), SwanlingError> {
2994        // Initialize per-user states.
2995        if self.attack_mode != AttackMode::Worker {
2996            // First run global test_start_task, if defined.
2997            match &self.test_start_task {
2998                Some(t) => {
2999                    info!("running test_start_task");
3000                    // Create a one-time-use User to run the test_start_task.
3001                    let base_url = swanling::get_base_url(
3002                        self.get_configuration_host(),
3003                        None,
3004                        self.defaults.host.clone(),
3005                    )?;
3006                    let user = SwanlingUser::single(base_url, &self.configuration)?;
3007                    let function = &t.function;
3008                    let _ = function(&user).await;
3009                }
3010                // No test_start_task defined, nothing to do.
3011                None => (),
3012            }
3013        }
3014
3015        Ok(())
3016    }
3017
3018    // Invoke `test_stop` tasks if existing.
3019    async fn run_test_stop(&self) -> Result<(), SwanlingError> {
3020        // Initialize per-user states.
3021        if self.attack_mode != AttackMode::Worker {
3022            // First run global test_stop_task, if defined.
3023            match &self.test_stop_task {
3024                Some(t) => {
3025                    info!("running test_stop_task");
3026                    // Create a one-time-use User to run the test_stop_task.
3027                    let base_url = swanling::get_base_url(
3028                        self.get_configuration_host(),
3029                        None,
3030                        self.defaults.host.clone(),
3031                    )?;
3032                    let user = SwanlingUser::single(base_url, &self.configuration)?;
3033                    let function = &t.function;
3034                    let _ = function(&user).await;
3035                }
3036                // No test_stop_task defined, nothing to do.
3037                None => (),
3038            }
3039        }
3040
3041        Ok(())
3042    }
3043
3044    // Create a SwanlingAttackRunState object and do all initialization required
3045    // to start a [`SwanlingAttack`](./struct.SwanlingAttack.html).
3046    async fn initialize_attack(
3047        &mut self,
3048        socket: Option<Socket>,
3049    ) -> Result<SwanlingAttackRunState, SwanlingError> {
3050        trace!("initialize_attack");
3051
3052        // Create a single channel used to send metrics from SwanlingUser threads
3053        // to parent thread.
3054        let (all_threads_metrics_tx, metrics_rx): (
3055            flume::Sender<SwanlingMetric>,
3056            flume::Receiver<SwanlingMetric>,
3057        ) = flume::unbounded();
3058
3059        // Optionally spawn a telnet and/or Websocket Controller thread.
3060        let controller_channel_rx = self.setup_controllers().await;
3061
3062        // Grab now() once from the standard library, used by multiple timers in
3063        // the run state.
3064        let std_now = std::time::Instant::now();
3065
3066        let swanling_attack_run_state = SwanlingAttackRunState {
3067            spawn_user_timer: std_now,
3068            spawn_user_in_ms: 0,
3069            spawn_user_counter: 0,
3070            drift_timer: tokio::time::Instant::now(),
3071            all_threads_metrics_tx,
3072            metrics_rx,
3073            logger_handle: None,
3074            all_threads_logger_tx: None,
3075            throttle_threads_tx: None,
3076            parent_to_throttle_tx: None,
3077            controller_channel_rx,
3078            report_file: None,
3079            metrics_header_displayed: false,
3080            idle_status_displayed: false,
3081            users: Vec::new(),
3082            user_channels: Vec::new(),
3083            running_metrics_timer: std_now,
3084            display_running_metrics: false,
3085            all_users_spawned: false,
3086            shutdown_after_stop: !self.configuration.no_autostart,
3087            canceled: Arc::new(AtomicBool::new(false)),
3088            socket,
3089        };
3090
3091        // Access socket to avoid errors.
3092        trace!("socket: {:?}", &swanling_attack_run_state.socket);
3093
3094        // Catch ctrl-c to allow clean shutdown to display metrics.
3095        util::setup_ctrlc_handler(&swanling_attack_run_state.canceled);
3096
3097        Ok(swanling_attack_run_state)
3098    }
3099
3100    // Spawn [`SwanlingUser`](./swanling/struct.SwanlingUser.html) threads to generate a
3101    // [`SwanlingAttack`](./struct.SwanlingAttack.html).
3102    async fn spawn_attack(
3103        &mut self,
3104        swanling_attack_run_state: &mut SwanlingAttackRunState,
3105    ) -> Result<(), SwanlingError> {
3106        // If the run_timer has expired, stop spawning user threads and start stopping them
3107        // instead. Unwrap is safe here because load test had to start to get here.
3108        if util::timer_expired(self.started.unwrap(), self.run_time) {
3109            self.set_attack_phase(swanling_attack_run_state, AttackPhase::Stopping);
3110            return Ok(());
3111        }
3112
3113        // Hatch rate is used to schedule the next user, and to ensure we don't
3114        // sleep too long.
3115        let hatch_rate = util::get_hatch_rate(self.configuration.hatch_rate.clone());
3116
3117        // Determine if it's time to spawn a SwanlingUser.
3118        if swanling_attack_run_state.spawn_user_in_ms == 0
3119            || util::ms_timer_expired(
3120                swanling_attack_run_state.spawn_user_timer,
3121                swanling_attack_run_state.spawn_user_in_ms,
3122            )
3123        {
3124            // Reset the spawn timer.
3125            swanling_attack_run_state.spawn_user_timer = std::time::Instant::now();
3126
3127            // To determine how long before we spawn the next SwanlingUser, start with 1,000.0
3128            // milliseconds and divide by the hatch_rate.
3129            swanling_attack_run_state.spawn_user_in_ms = (1_000.0 / hatch_rate) as usize;
3130
3131            // If running on a Worker, multiple by the number of workers as each is spawning
3132            // SwanlingUsers at this rate.
3133            if self.attack_mode == AttackMode::Worker {
3134                swanling_attack_run_state.spawn_user_in_ms *=
3135                    self.configuration.expect_workers.unwrap() as usize;
3136            }
3137
3138            // Spawn next scheduled SwanlingUser.
3139            let mut thread_user =
3140                self.weighted_users[swanling_attack_run_state.spawn_user_counter].clone();
3141            swanling_attack_run_state.spawn_user_counter += 1;
3142
3143            // Copy weighted tasks and weighted on start tasks into the user thread.
3144            thread_user.weighted_tasks = self.task_sets[thread_user.task_sets_index]
3145                .weighted_tasks
3146                .clone();
3147            thread_user.weighted_on_start_tasks = self.task_sets[thread_user.task_sets_index]
3148                .weighted_on_start_tasks
3149                .clone();
3150            thread_user.weighted_on_stop_tasks = self.task_sets[thread_user.task_sets_index]
3151                .weighted_on_stop_tasks
3152                .clone();
3153            // Remember which task group this user is using.
3154            thread_user.weighted_users_index = self.metrics.users;
3155
3156            // Create a per-thread channel allowing parent thread to control child threads.
3157            let (parent_sender, thread_receiver): (
3158                flume::Sender<SwanlingUserCommand>,
3159                flume::Receiver<SwanlingUserCommand>,
3160            ) = flume::unbounded();
3161            swanling_attack_run_state.user_channels.push(parent_sender);
3162
3163            // Clone the logger_tx if enabled, otherwise is None.
3164            thread_user.logger = swanling_attack_run_state.all_threads_logger_tx.clone();
3165
3166            // Copy the SwanlingUser-throttle receiver channel, used by all threads.
3167            thread_user.throttle = if self.configuration.throttle_requests > 0 {
3168                Some(
3169                    swanling_attack_run_state
3170                        .throttle_threads_tx
3171                        .clone()
3172                        .unwrap(),
3173                )
3174            } else {
3175                None
3176            };
3177
3178            // Copy the SwanlingUser-to-parent sender channel, used by all threads.
3179            thread_user.channel_to_parent =
3180                Some(swanling_attack_run_state.all_threads_metrics_tx.clone());
3181
3182            // Copy the appropriate task_set into the thread.
3183            let thread_task_set = self.task_sets[thread_user.task_sets_index].clone();
3184
3185            // We number threads from 1 as they're human-visible (in the logs),
3186            // whereas metrics.users starts at 0.
3187            let thread_number = self.metrics.users + 1;
3188
3189            let is_worker = self.attack_mode == AttackMode::Worker;
3190
3191            // If running on Worker, use Worker configuration in SwanlingUser.
3192            if is_worker {
3193                thread_user.config = self.configuration.clone();
3194            }
3195
3196            // Launch a new user.
3197            let user = tokio::spawn(user::user_main(
3198                thread_number,
3199                thread_task_set,
3200                thread_user,
3201                thread_receiver,
3202                is_worker,
3203            ));
3204
3205            swanling_attack_run_state.users.push(user);
3206            self.metrics.users += 1;
3207
3208            if let Some(running_metrics) = self.configuration.running_metrics {
3209                if self.attack_mode != AttackMode::Worker
3210                    && util::timer_expired(
3211                        swanling_attack_run_state.running_metrics_timer,
3212                        running_metrics,
3213                    )
3214                {
3215                    swanling_attack_run_state.running_metrics_timer = time::Instant::now();
3216                    self.metrics.print_running();
3217                }
3218            }
3219        } else {
3220            // If displaying running metrics, be sure we wake up often enough to
3221            // display them at the configured rate.
3222            let running_metrics = self.configuration.running_metrics.unwrap_or(0);
3223
3224            // Otherwise, sleep until the next time something needs to happen.
3225            let sleep_duration = if running_metrics > 0
3226                && running_metrics * 1_000 < swanling_attack_run_state.spawn_user_in_ms
3227            {
3228                let sleep_delay = self.configuration.running_metrics.unwrap() * 1_000;
3229                swanling_attack_run_state.spawn_user_in_ms -= sleep_delay;
3230                tokio::time::Duration::from_millis(sleep_delay as u64)
3231            } else {
3232                tokio::time::Duration::from_millis(
3233                    swanling_attack_run_state.spawn_user_in_ms as u64,
3234                )
3235            };
3236            debug!("sleeping {:?}...", sleep_duration);
3237            swanling_attack_run_state.drift_timer =
3238                util::sleep_minus_drift(sleep_duration, swanling_attack_run_state.drift_timer)
3239                    .await;
3240        }
3241
3242        // If enough users have been spawned, move onto the next attack phase.
3243        if self.metrics.users >= self.weighted_users.len() {
3244            // Pause a tenth of a second waiting for the final user to fully start up.
3245            tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
3246
3247            if self.attack_mode == AttackMode::Worker {
3248                info!(
3249                    "[{}] launched {} users...",
3250                    get_worker_id(),
3251                    self.metrics.users
3252                );
3253            } else {
3254                info!("launched {} users...", self.metrics.users);
3255            }
3256
3257            self.reset_metrics(swanling_attack_run_state).await?;
3258            self.set_attack_phase(swanling_attack_run_state, AttackPhase::Running);
3259        }
3260
3261        Ok(())
3262    }
3263
3264    // Let the [`SwanlingAttack`](./struct.SwanlingAttack.html) run until the timer expires
3265    // (or the test is canceled), and then trigger a shut down.
3266    async fn monitor_attack(
3267        &mut self,
3268        swanling_attack_run_state: &mut SwanlingAttackRunState,
3269    ) -> Result<(), SwanlingError> {
3270        // Exit if run_time timer expires.
3271        if util::timer_expired(self.started.unwrap(), self.run_time) {
3272            self.set_attack_phase(swanling_attack_run_state, AttackPhase::Stopping);
3273        } else {
3274            // Subtract the time spent doing other things, running the main parent loop twice
3275            // per second.
3276            swanling_attack_run_state.drift_timer = util::sleep_minus_drift(
3277                time::Duration::from_millis(500),
3278                swanling_attack_run_state.drift_timer,
3279            )
3280            .await;
3281        }
3282
3283        Ok(())
3284    }
3285
3286    async fn stop_running_users(
3287        &mut self,
3288        swanling_attack_run_state: &mut SwanlingAttackRunState,
3289    ) -> Result<(), SwanlingError> {
3290        if self.attack_mode == AttackMode::Worker {
3291            info!(
3292                "[{}] stopping after {} seconds...",
3293                get_worker_id(),
3294                self.metrics.duration
3295            );
3296
3297            // Load test is shutting down, update pipe handler so there is no panic
3298            // when the Manager goes away.
3299            #[cfg(feature = "gaggle")]
3300            {
3301                let manager = swanling_attack_run_state.socket.clone().unwrap();
3302                register_shutdown_pipe_handler(&manager);
3303            }
3304        } else {
3305            info!("stopping after {} seconds...", self.metrics.duration);
3306        }
3307        for (index, send_to_user) in swanling_attack_run_state.user_channels.iter().enumerate() {
3308            match send_to_user.send(SwanlingUserCommand::Exit) {
3309                Ok(_) => {
3310                    debug!("telling user {} to exit", index);
3311                }
3312                Err(e) => {
3313                    info!("failed to tell user {} to exit: {}", index, e);
3314                }
3315            }
3316        }
3317        if self.attack_mode == AttackMode::Worker {
3318            info!("[{}] waiting for users to exit", get_worker_id());
3319        } else {
3320            info!("waiting for users to exit");
3321        }
3322
3323        // If throttle is enabled, tell throttle thread the load test is over.
3324        if let Some(throttle_tx) = swanling_attack_run_state.parent_to_throttle_tx.clone() {
3325            let _ = throttle_tx.send(false);
3326        }
3327
3328        // Take the users vector out of the SwanlingAttackRunState object so it can be
3329        // consumed by futures::future::join_all().
3330        let users = std::mem::take(&mut swanling_attack_run_state.users);
3331        futures::future::join_all(users).await;
3332        debug!("all users exited");
3333
3334        // If the logger thread is enabled, tell it to flush and exit.
3335        if swanling_attack_run_state.logger_handle.is_some() {
3336            if let Err(e) = swanling_attack_run_state
3337                .all_threads_logger_tx
3338                .clone()
3339                .unwrap()
3340                .send(None)
3341            {
3342                warn!("unexpected error telling logger thread to exit: {}", e);
3343            };
3344            // Take logger out of the SwanlingAttackRunState object so it can be
3345            // consumed by tokio::join!().
3346            let logger = std::mem::take(&mut swanling_attack_run_state.logger_handle);
3347            let _ = tokio::join!(logger.unwrap());
3348        }
3349
3350        // If we're printing metrics, collect the final metrics received from users.
3351        if !self.configuration.no_metrics {
3352            // Set the second parameter to true, ensuring that Swanling waits until all metrics
3353            // are received.
3354            let _received_message = self
3355                .receive_metrics(swanling_attack_run_state, true)
3356                .await?;
3357        }
3358
3359        #[cfg(feature = "gaggle")]
3360        {
3361            // As worker, push metrics up to manager.
3362            if self.attack_mode == AttackMode::Worker {
3363                worker::push_metrics_to_manager(
3364                    &swanling_attack_run_state.socket.clone().unwrap(),
3365                    vec![
3366                        GaggleMetrics::Requests(self.metrics.requests.clone()),
3367                        GaggleMetrics::Errors(self.metrics.errors.clone()),
3368                        GaggleMetrics::Tasks(self.metrics.tasks.clone()),
3369                    ],
3370                    true,
3371                );
3372                // No need to reset local metrics, the worker is exiting.
3373            }
3374        }
3375
3376        Ok(())
3377    }
3378
3379    // Cleanly shut down the [`SwanlingAttack`](./struct.SwanlingAttack.html).
3380    async fn stop_attack(&mut self) -> Result<(), SwanlingError> {
3381        // Run any configured test_stop() functions.
3382        self.run_test_stop().await?;
3383
3384        // Percentile and errors are only displayed when the load test is finished.
3385        self.metrics.final_metrics = true;
3386
3387        Ok(())
3388    }
3389
3390    // Reset the SwanlingAttackRunState before starting a load test. This is to allow a Controller
3391    // to stop and start the load test multiple times, for example from a UI.
3392    async fn reset_run_state(
3393        &mut self,
3394        swanling_attack_run_state: &mut SwanlingAttackRunState,
3395    ) -> Result<(), SwanlingError> {
3396        // Run any configured test_start() functions.
3397        self.run_test_start().await.unwrap();
3398
3399        // Prepare to collect metrics, if enabled.
3400        self.metrics = SwanlingMetrics::default();
3401        if !self.configuration.no_metrics {
3402            self.metrics
3403                .initialize_task_metrics(&self.task_sets, &self.configuration);
3404            self.metrics.display_metrics = true;
3405            // Only display status codes if enabled.
3406            self.metrics.display_status_codes = self.configuration.status_codes;
3407        }
3408
3409        // Reset the run state.
3410        let std_now = std::time::Instant::now();
3411        swanling_attack_run_state.spawn_user_timer = std_now;
3412        swanling_attack_run_state.spawn_user_in_ms = 0;
3413        swanling_attack_run_state.spawn_user_counter = 0;
3414        swanling_attack_run_state.drift_timer = tokio::time::Instant::now();
3415        swanling_attack_run_state.metrics_header_displayed = false;
3416        swanling_attack_run_state.idle_status_displayed = false;
3417        swanling_attack_run_state.users = Vec::new();
3418        swanling_attack_run_state.user_channels = Vec::new();
3419        swanling_attack_run_state.running_metrics_timer = std_now;
3420        swanling_attack_run_state.display_running_metrics = false;
3421        swanling_attack_run_state.shutdown_after_stop = !self.configuration.no_autostart;
3422        swanling_attack_run_state.all_users_spawned = false;
3423
3424        // If enabled, spawn a logger thread.
3425        let (logger_handle, all_threads_logger_tx) =
3426            self.configuration.setup_loggers(&self.defaults).await?;
3427        swanling_attack_run_state.logger_handle = logger_handle;
3428        swanling_attack_run_state.all_threads_logger_tx = all_threads_logger_tx;
3429
3430        // If enabled, spawn a throttle thread.
3431        let (throttle_threads_tx, parent_to_throttle_tx) = self.setup_throttle().await;
3432        swanling_attack_run_state.throttle_threads_tx = throttle_threads_tx;
3433        swanling_attack_run_state.parent_to_throttle_tx = parent_to_throttle_tx;
3434
3435        // If enabled, create an report file and confirm access.
3436        swanling_attack_run_state.report_file = match self.prepare_report_file().await {
3437            Ok(f) => f,
3438            Err(e) => {
3439                return Err(SwanlingError::InvalidOption {
3440                    option: "--report-file".to_string(),
3441                    value: self.get_report_file_path().unwrap(),
3442                    detail: format!("Failed to create report file: {}", e),
3443                })
3444            }
3445        };
3446
3447        // Record when the SwanlingAttack officially started.
3448        self.started = Some(time::Instant::now());
3449
3450        // Also record a formattable timestamp, for human readable reports.
3451        self.metrics.started = Some(Local::now());
3452
3453        Ok(())
3454    }
3455
3456    // Called internally in local-mode and gaggle-mode.
3457    async fn start_attack(
3458        mut self,
3459        socket: Option<Socket>,
3460    ) -> Result<SwanlingAttack, SwanlingError> {
3461        trace!("start_attack: socket({:?})", socket);
3462
3463        // The SwanlingAttackRunState is used while spawning and running the
3464        // SwanlingUser threads that generate the load test.
3465        let mut swanling_attack_run_state = self
3466            .initialize_attack(socket)
3467            .await
3468            .expect("failed to initialize SwanlingAttackRunState");
3469
3470        // The Swanling parent process SwanlingAttack loop runs until Swanling shuts down. Swanling enters
3471        // the loop in AttackPhase::Idle, and exits in AttackPhase::Shutdown.
3472        loop {
3473            match self.attack_phase {
3474                // In the Idle phase the Swanling configuration can be changed by a Controller,
3475                // and otherwise nothing happens but sleeping an checking for messages.
3476                AttackPhase::Idle => {
3477                    if self.configuration.no_autostart {
3478                        // Sleep then check for further instructions.
3479                        if swanling_attack_run_state.idle_status_displayed {
3480                            let sleep_duration = tokio::time::Duration::from_millis(250);
3481                            debug!("sleeping {:?}...", sleep_duration);
3482                            swanling_attack_run_state.drift_timer = util::sleep_minus_drift(
3483                                sleep_duration,
3484                                swanling_attack_run_state.drift_timer,
3485                            )
3486                            .await;
3487                        // Only display informational message about being idle one time.
3488                        } else {
3489                            info!("Swanling is currently idle.");
3490                            swanling_attack_run_state.idle_status_displayed = true;
3491                        }
3492                    } else {
3493                        // Prepare to start the load test, resetting timers and counters.
3494                        self.reset_run_state(&mut swanling_attack_run_state).await?;
3495                        self.set_attack_phase(
3496                            &mut swanling_attack_run_state,
3497                            AttackPhase::Starting,
3498                        );
3499                    }
3500                }
3501                // In the Start phase, Swanling launches SwanlingUser threads and starts a SwanlingAttack.
3502                AttackPhase::Starting => {
3503                    self.update_duration();
3504                    self.spawn_attack(&mut swanling_attack_run_state)
3505                        .await
3506                        .expect("failed to start SwanlingAttack");
3507                }
3508                // In the Running phase, Swanling maintains the configured SwanlingAttack.
3509                AttackPhase::Running => {
3510                    self.update_duration();
3511                    self.monitor_attack(&mut swanling_attack_run_state).await?;
3512                }
3513                // In the Stopping phase, Swanling stops all SwanlingUser threads and optionally reports
3514                // any collected metrics.
3515                AttackPhase::Stopping => {
3516                    // If displaying metrics, update internal state reflecting how long load test
3517                    // has been running.
3518                    self.update_duration();
3519                    // Tell all running SwanlingUsers to stop.
3520                    self.stop_running_users(&mut swanling_attack_run_state)
3521                        .await?;
3522                    // Stop any running SwanlingUser threads.
3523                    self.stop_attack().await?;
3524                    // Collect all metrics sent by SwanlingUser threads.
3525                    self.sync_metrics(&mut swanling_attack_run_state, true)
3526                        .await?;
3527                    // Write an html report, if enabled.
3528                    self.write_html_report(&mut swanling_attack_run_state)
3529                        .await?;
3530                    // Shutdown Swanling or go into an idle waiting state.
3531                    if swanling_attack_run_state.shutdown_after_stop {
3532                        self.set_attack_phase(
3533                            &mut swanling_attack_run_state,
3534                            AttackPhase::Shutdown,
3535                        );
3536                    } else {
3537                        // Print metrics, if enabled.
3538                        if !self.configuration.no_metrics {
3539                            println!("{}", self.metrics);
3540                        }
3541                        self.set_attack_phase(&mut swanling_attack_run_state, AttackPhase::Idle);
3542                    }
3543                }
3544                // By reaching the Shutdown phase, break out of the SwanlingAttack loop.
3545                AttackPhase::Shutdown => break,
3546            }
3547            // Regularly synchronize metrics.
3548            self.sync_metrics(&mut swanling_attack_run_state, false)
3549                .await?;
3550
3551            // Check if a Controller has made a request.
3552            self.handle_controller_requests(&mut swanling_attack_run_state)
3553                .await?;
3554
3555            // Gracefully exit loop if ctrl-c is caught.
3556            if self.attack_phase != AttackPhase::Shutdown
3557                && swanling_attack_run_state.canceled.load(Ordering::SeqCst)
3558            {
3559                // Shutdown after stopping as the load test was canceled.
3560                swanling_attack_run_state.shutdown_after_stop = true;
3561
3562                // No metrics to display when sitting idle, so disable.
3563                if self.attack_phase == AttackPhase::Idle {
3564                    self.metrics.display_metrics = false;
3565                }
3566
3567                // Cleanly stop the load test.
3568                self.set_attack_phase(&mut swanling_attack_run_state, AttackPhase::Stopping);
3569            }
3570        }
3571
3572        Ok(self)
3573    }
3574}
3575
3576/// All run-time options can optionally be configured with custom defaults.
3577///
3578/// For example, you can optionally configure a default host for the load test. This is
3579/// used if no per-[`SwanlingTaskSet`](./swanling/struct.SwanlingTaskSet.html) host is defined,
3580/// no `--host` CLI option is configured, and if the
3581/// [`SwanlingTask`](./swanling/struct.SwanlingTask.html) itself doesn't hard-code the host in
3582/// the base url of its request. In that case, this host is added to all requests.
3583///
3584/// For example, a load test could be configured to default to running against a local
3585/// development container, and the `--host` option could be used to override the host
3586/// value to run the load test against the production environment.
3587///
3588/// # Example
3589/// ```rust
3590/// use swanling::prelude::*;
3591///
3592/// fn main() -> Result<(), SwanlingError> {
3593///     SwanlingAttack::initialize()?
3594///         .set_default(SwanlingDefault::Host, "local.dev")?;
3595///
3596///     Ok(())
3597/// }
3598/// ```
3599///
3600/// The following run-time options can be configured with a custom default using a
3601/// borrowed string slice (`&str`):
3602///  - [SwanlingDefault::Host](../swanling/enum.SwanlingDefault.html#variant.Host)
3603///  - [SwanlingDefault::SwanlingLog](../swanling/enum.SwanlingDefault.html#variant.SwanlingLog)
3604///  - [SwanlingDefault::RequestFormat](../swanling/enum.SwanlingDefault.html#variant.RequestFormat)
3605///  - [SwanlingDefault::TaskLog](../swanling/enum.SwanlingDefault.html#variant.TaskLog)
3606///  - [SwanlingDefault::ErrorLog](../swanling/enum.SwanlingDefault.html#variant.ErrorLog)
3607///  - [SwanlingDefault::DebugLog](../swanling/enum.SwanlingDefault.html#variant.DebugLog)
3608///  - [SwanlingDefault::TelnetHost](../swanling/enum.SwanlingDefault.html#variant.TelnetHost)
3609///  - [SwanlingDefault::WebSocketHost](../swanling/enum.SwanlingDefault.html#variant.WebSocketHost)
3610///  - [SwanlingDefault::ManagerBindHost](../swanling/enum.SwanlingDefault.html#variant.ManagerBindHost)
3611///  - [SwanlingDefault::ManagerHost](../swanling/enum.SwanlingDefault.html#variant.ManagerHost)
3612///
3613/// The following run-time options can be configured with a custom default using a
3614/// `usize` integer:
3615///  - [SwanlingDefault::Users](../swanling/enum.SwanlingDefault.html#variant.Users)
3616///  - [SwanlingDefault::HatchRate](../swanling/enum.SwanlingDefault.html#variant.HatchRate)
3617///  - [SwanlingDefault::RunTime](../swanling/enum.SwanlingDefault.html#variant.RunTime)
3618///  - [SwanlingDefault::RunningMetrics](../swanling/enum.SwanlingDefault.html#variant.RunningMetrics)
3619///  - [SwanlingDefault::LogLevel](../swanling/enum.SwanlingDefault.html#variant.LogLevel)
3620///  - [SwanlingDefault::Verbose](../swanling/enum.SwanlingDefault.html#variant.Verbose)
3621///  - [SwanlingDefault::ThrottleRequests](../swanling/enum.SwanlingDefault.html#variant.ThrottleRequests)
3622///  - [SwanlingDefault::ExpectWorkers](../swanling/enum.SwanlingDefault.html#variant.ExpectWorkers)
3623///  - [SwanlingDefault::TelnetPort](../swanling/enum.SwanlingDefault.html#variant.TelnetPort)
3624///  - [SwanlingDefault::WebSocketPort](../swanling/enum.SwanlingDefault.html#variant.WebSocketPort)
3625///  - [SwanlingDefault::ManagerBindPort](../swanling/enum.SwanlingDefault.html#variant.ManagerBindPort)
3626///  - [SwanlingDefault::ManagerPort](../swanling/enum.SwanlingDefault.html#variant.ManagerPort)
3627///
3628/// The following run-time flags can be configured with a custom default using a
3629/// `bool` (and otherwise default to `false`).
3630///  - [SwanlingDefault::NoResetMetrics](../swanling/enum.SwanlingDefault.html#variant.NoResetMetrics)
3631///  - [SwanlingDefault::NoMetrics](../swanling/enum.SwanlingDefault.html#variant.NoMetrics)
3632///  - [SwanlingDefault::NoTaskMetrics](../swanling/enum.SwanlingDefault.html#variant.NoTaskMetrics)
3633///  - [SwanlingDefault::NoErrorSummary](../swanling/enum.SwanlingDefault.html#variant.NoErrorSummary)
3634///  - [SwanlingDefault::NoDebugBody](../swanling/enum.SwanlingDefault.html#variant.NoDebugBody)
3635///  - [SwanlingDefault::NoTelnet](../swanling/enum.SwanlingDefault.html#variant.NoTelnet)
3636///  - [SwanlingDefault::NoWebSocket](../swanling/enum.SwanlingDefault.html#variant.NoWebSocket)
3637///  - [SwanlingDefault::NoAutoStart](../swanling/enum.SwanlingDefault.html#variant.NoAutoStart)
3638///  - [SwanlingDefault::StatusCodes](../swanling/enum.SwanlingDefault.html#variant.StatusCodes)
3639///  - [SwanlingDefault::StickyFollow](../swanling/enum.SwanlingDefault.html#variant.StickyFollow)
3640///  - [SwanlingDefault::Manager](../swanling/enum.SwanlingDefault.html#variant.Manager)
3641///  - [SwanlingDefault::NoHashCheck](../swanling/enum.SwanlingDefault.html#variant.NoHashCheck)
3642///  - [SwanlingDefault::Worker](../swanling/enum.SwanlingDefault.html#variant.Worker)
3643///
3644/// The following run-time flags can be configured with a custom default using a
3645/// `SwanlingLogFormat`.
3646///  - [SwanlingDefault::RequestLog](../swanling/enum.SwanlingDefault.html#variant.RequestLog)
3647///  - [SwanlingDefault::TaskLog](../swanling/enum.SwanlingDefault.html#variant.TaskLog)
3648///  - [SwanlingDefault::DebugFormat](../swanling/enum.SwanlingDefault.html#variant.DebugFormat)
3649/// # Another Example
3650/// ```rust
3651/// use swanling::prelude::*;
3652///
3653/// fn main() -> Result<(), SwanlingError> {
3654///     SwanlingAttack::initialize()?
3655///         // Do not reset the metrics after the load test finishes starting.
3656///         .set_default(SwanlingDefault::NoResetMetrics, true)?
3657///         // Display info level logs while the test runs.
3658///         .set_default(SwanlingDefault::Verbose, 1)?
3659///         // Log all requests made during the test to `./swanling-request.log`.
3660///         .set_default(SwanlingDefault::RequestLog, "swanling-request.log")?;
3661///
3662///     Ok(())
3663/// }
3664/// ```
3665pub trait SwanlingDefaultType<T> {
3666    fn set_default(self, key: SwanlingDefault, value: T) -> Result<Box<Self>, SwanlingError>;
3667}
3668impl SwanlingDefaultType<&str> for SwanlingAttack {
3669    fn set_default(
3670        mut self,
3671        key: SwanlingDefault,
3672        value: &str,
3673    ) -> Result<Box<Self>, SwanlingError> {
3674        match key {
3675            // Set valid defaults.
3676            SwanlingDefault::HatchRate => self.defaults.hatch_rate = Some(value.to_string()),
3677            SwanlingDefault::Host => self.defaults.host = Some(value.to_string()),
3678            SwanlingDefault::SwanlingLog => self.defaults.swanling_log = Some(value.to_string()),
3679            SwanlingDefault::ReportFile => self.defaults.report_file = Some(value.to_string()),
3680            SwanlingDefault::RequestLog => self.defaults.request_log = Some(value.to_string()),
3681            SwanlingDefault::TaskLog => self.defaults.task_log = Some(value.to_string()),
3682            SwanlingDefault::ErrorLog => self.defaults.error_log = Some(value.to_string()),
3683            SwanlingDefault::DebugLog => self.defaults.debug_log = Some(value.to_string()),
3684            SwanlingDefault::TelnetHost => self.defaults.telnet_host = Some(value.to_string()),
3685            SwanlingDefault::WebSocketHost => {
3686                self.defaults.websocket_host = Some(value.to_string())
3687            }
3688            SwanlingDefault::ManagerBindHost => {
3689                self.defaults.manager_bind_host = Some(value.to_string())
3690            }
3691            SwanlingDefault::ManagerHost => self.defaults.manager_host = Some(value.to_string()),
3692            // Otherwise display a helpful and explicit error.
3693            SwanlingDefault::Users
3694            | SwanlingDefault::RunTime
3695            | SwanlingDefault::LogLevel
3696            | SwanlingDefault::Verbose
3697            | SwanlingDefault::ThrottleRequests
3698            | SwanlingDefault::ExpectWorkers
3699            | SwanlingDefault::TelnetPort
3700            | SwanlingDefault::WebSocketPort
3701            | SwanlingDefault::ManagerBindPort
3702            | SwanlingDefault::ManagerPort => {
3703                return Err(SwanlingError::InvalidOption {
3704                    option: format!("SwanlingDefault::{:?}", key),
3705                    value: value.to_string(),
3706                    detail: format!(
3707                        "set_default(SwanlingDefault::{:?}, {}) expected usize value, received &str",
3708                        key, value
3709                    ),
3710                });
3711            }
3712            SwanlingDefault::RunningMetrics
3713            | SwanlingDefault::NoResetMetrics
3714            | SwanlingDefault::NoMetrics
3715            | SwanlingDefault::NoTaskMetrics
3716            | SwanlingDefault::NoErrorSummary
3717            | SwanlingDefault::NoDebugBody
3718            | SwanlingDefault::NoTelnet
3719            | SwanlingDefault::NoWebSocket
3720            | SwanlingDefault::NoAutoStart
3721            | SwanlingDefault::StatusCodes
3722            | SwanlingDefault::StickyFollow
3723            | SwanlingDefault::Manager
3724            | SwanlingDefault::NoHashCheck
3725            | SwanlingDefault::Worker => {
3726                return Err(SwanlingError::InvalidOption {
3727                    option: format!("SwanlingDefault::{:?}", key),
3728                    value: value.to_string(),
3729                    detail: format!(
3730                        "set_default(SwanlingDefault::{:?}, {}) expected bool value, received &str",
3731                        key, value
3732                    ),
3733                });
3734            }
3735            SwanlingDefault::DebugFormat
3736            | SwanlingDefault::ErrorFormat
3737            | SwanlingDefault::TaskFormat
3738            | SwanlingDefault::RequestFormat => {
3739                return Err(SwanlingError::InvalidOption {
3740                    option: format!("SwanlingDefault::{:?}", key),
3741                    value: value.to_string(),
3742                    detail: format!(
3743                        "set_default(SwanlingDefault::{:?}, {}) expected SwanlingLogFormat value, received &str",
3744                        key, value
3745                    ),
3746                });
3747            }
3748            SwanlingDefault::CoordinatedOmissionMitigation => {
3749                return Err(SwanlingError::InvalidOption {
3750                    option: format!("SwanlingDefault::{:?}", key),
3751                    value: value.to_string(),
3752                    detail: format!(
3753                        "set_default(SwanlingDefault::{:?}, {}) expected SwanlingCoordinatedOmissionMitigation value, received &str",
3754                        key, value
3755                    ),
3756                });
3757            }
3758        }
3759        Ok(Box::new(self))
3760    }
3761}
3762impl SwanlingDefaultType<usize> for SwanlingAttack {
3763    fn set_default(
3764        mut self,
3765        key: SwanlingDefault,
3766        value: usize,
3767    ) -> Result<Box<Self>, SwanlingError> {
3768        match key {
3769            SwanlingDefault::Users => self.defaults.users = Some(value),
3770            SwanlingDefault::RunTime => self.defaults.run_time = Some(value),
3771            SwanlingDefault::RunningMetrics => self.defaults.running_metrics = Some(value),
3772            SwanlingDefault::LogLevel => self.defaults.log_level = Some(value as u8),
3773            SwanlingDefault::Verbose => self.defaults.verbose = Some(value as u8),
3774            SwanlingDefault::ThrottleRequests => self.defaults.throttle_requests = Some(value),
3775            SwanlingDefault::ExpectWorkers => self.defaults.expect_workers = Some(value as u16),
3776            SwanlingDefault::TelnetPort => self.defaults.telnet_port = Some(value as u16),
3777            SwanlingDefault::WebSocketPort => self.defaults.websocket_port = Some(value as u16),
3778            SwanlingDefault::ManagerBindPort => {
3779                self.defaults.manager_bind_port = Some(value as u16)
3780            }
3781            SwanlingDefault::ManagerPort => self.defaults.manager_port = Some(value as u16),
3782            // Otherwise display a helpful and explicit error.
3783            SwanlingDefault::Host
3784            | SwanlingDefault::HatchRate
3785            | SwanlingDefault::SwanlingLog
3786            | SwanlingDefault::ReportFile
3787            | SwanlingDefault::RequestLog
3788            | SwanlingDefault::TaskLog
3789            | SwanlingDefault::ErrorLog
3790            | SwanlingDefault::DebugLog
3791            | SwanlingDefault::TelnetHost
3792            | SwanlingDefault::WebSocketHost
3793            | SwanlingDefault::ManagerBindHost
3794            | SwanlingDefault::ManagerHost => {
3795                return Err(SwanlingError::InvalidOption {
3796                    option: format!("SwanlingDefault::{:?}", key),
3797                    value: format!("{}", value),
3798                    detail: format!(
3799                    "set_default(SwanlingDefault::{:?}, {}) expected &str value, received usize",
3800                    key, value
3801                ),
3802                })
3803            }
3804            SwanlingDefault::NoResetMetrics
3805            | SwanlingDefault::NoMetrics
3806            | SwanlingDefault::NoTaskMetrics
3807            | SwanlingDefault::NoErrorSummary
3808            | SwanlingDefault::NoDebugBody
3809            | SwanlingDefault::NoTelnet
3810            | SwanlingDefault::NoWebSocket
3811            | SwanlingDefault::NoAutoStart
3812            | SwanlingDefault::StatusCodes
3813            | SwanlingDefault::StickyFollow
3814            | SwanlingDefault::Manager
3815            | SwanlingDefault::NoHashCheck
3816            | SwanlingDefault::Worker => {
3817                return Err(SwanlingError::InvalidOption {
3818                    option: format!("SwanlingDefault::{:?}", key),
3819                    value: format!("{}", value),
3820                    detail: format!(
3821                    "set_default(SwanlingDefault::{:?}, {}) expected bool value, received usize",
3822                    key, value
3823                ),
3824                })
3825            }
3826            SwanlingDefault::RequestFormat
3827            | SwanlingDefault::DebugFormat
3828            | SwanlingDefault::ErrorFormat
3829            | SwanlingDefault::TaskFormat => {
3830                return Err(SwanlingError::InvalidOption {
3831                    option: format!("SwanlingDefault::{:?}", key),
3832                    value: value.to_string(),
3833                    detail: format!(
3834                        "set_default(SwanlingDefault::{:?}, {}) expected SwanlingLogFormat value, received usize",
3835                        key, value
3836                    ),
3837                });
3838            }
3839            SwanlingDefault::CoordinatedOmissionMitigation => {
3840                return Err(SwanlingError::InvalidOption {
3841                    option: format!("SwanlingDefault::{:?}", key),
3842                    value: value.to_string(),
3843                    detail: format!(
3844                        "set_default(SwanlingDefault::{:?}, {}) expected SwanlingCoordinatedOmissionMitigation value, received usize",
3845                        key, value
3846                    ),
3847                });
3848            }
3849        }
3850        Ok(Box::new(self))
3851    }
3852}
3853impl SwanlingDefaultType<bool> for SwanlingAttack {
3854    fn set_default(
3855        mut self,
3856        key: SwanlingDefault,
3857        value: bool,
3858    ) -> Result<Box<Self>, SwanlingError> {
3859        match key {
3860            SwanlingDefault::NoResetMetrics => self.defaults.no_reset_metrics = Some(value),
3861            SwanlingDefault::NoMetrics => self.defaults.no_metrics = Some(value),
3862            SwanlingDefault::NoTaskMetrics => self.defaults.no_task_metrics = Some(value),
3863            SwanlingDefault::NoErrorSummary => self.defaults.no_error_summary = Some(value),
3864            SwanlingDefault::NoDebugBody => self.defaults.no_debug_body = Some(value),
3865            SwanlingDefault::NoTelnet => self.defaults.no_telnet = Some(value),
3866            SwanlingDefault::NoWebSocket => self.defaults.no_websocket = Some(value),
3867            SwanlingDefault::NoAutoStart => self.defaults.no_autostart = Some(value),
3868            SwanlingDefault::StatusCodes => self.defaults.status_codes = Some(value),
3869            SwanlingDefault::StickyFollow => self.defaults.sticky_follow = Some(value),
3870            SwanlingDefault::Manager => self.defaults.manager = Some(value),
3871            SwanlingDefault::NoHashCheck => self.defaults.no_hash_check = Some(value),
3872            SwanlingDefault::Worker => self.defaults.worker = Some(value),
3873            // Otherwise display a helpful and explicit error.
3874            SwanlingDefault::Host
3875            | SwanlingDefault::SwanlingLog
3876            | SwanlingDefault::ReportFile
3877            | SwanlingDefault::RequestLog
3878            | SwanlingDefault::TaskLog
3879            | SwanlingDefault::RunningMetrics
3880            | SwanlingDefault::ErrorLog
3881            | SwanlingDefault::DebugLog
3882            | SwanlingDefault::TelnetHost
3883            | SwanlingDefault::WebSocketHost
3884            | SwanlingDefault::ManagerBindHost
3885            | SwanlingDefault::ManagerHost => {
3886                return Err(SwanlingError::InvalidOption {
3887                    option: format!("SwanlingDefault::{:?}", key),
3888                    value: format!("{}", value),
3889                    detail: format!(
3890                        "set_default(SwanlingDefault::{:?}, {}) expected &str value, received bool",
3891                        key, value
3892                    ),
3893                })
3894            }
3895            SwanlingDefault::Users
3896            | SwanlingDefault::HatchRate
3897            | SwanlingDefault::RunTime
3898            | SwanlingDefault::LogLevel
3899            | SwanlingDefault::Verbose
3900            | SwanlingDefault::ThrottleRequests
3901            | SwanlingDefault::ExpectWorkers
3902            | SwanlingDefault::TelnetPort
3903            | SwanlingDefault::WebSocketPort
3904            | SwanlingDefault::ManagerBindPort
3905            | SwanlingDefault::ManagerPort => {
3906                return Err(SwanlingError::InvalidOption {
3907                    option: format!("SwanlingDefault::{:?}", key),
3908                    value: format!("{}", value),
3909                    detail: format!(
3910                    "set_default(SwanlingDefault::{:?}, {}) expected usize value, received bool",
3911                    key, value
3912                ),
3913                })
3914            }
3915            SwanlingDefault::RequestFormat
3916            | SwanlingDefault::DebugFormat
3917            | SwanlingDefault::ErrorFormat
3918            | SwanlingDefault::TaskFormat => {
3919                return Err(SwanlingError::InvalidOption {
3920                    option: format!("SwanlingDefault::{:?}", key),
3921                    value: value.to_string(),
3922                    detail: format!(
3923                        "set_default(SwanlingDefault::{:?}, {}) expected SwanlingLogFormat value, received bool",
3924                        key, value
3925                    ),
3926                });
3927            }
3928            SwanlingDefault::CoordinatedOmissionMitigation => {
3929                return Err(SwanlingError::InvalidOption {
3930                    option: format!("SwanlingDefault::{:?}", key),
3931                    value: value.to_string(),
3932                    detail: format!(
3933                        "set_default(SwanlingDefault::{:?}, {}) expected SwanlingCoordinatedOmissionMitigation value, received bool",
3934                        key, value
3935                    ),
3936                });
3937            }
3938        }
3939        Ok(Box::new(self))
3940    }
3941}
3942impl SwanlingDefaultType<SwanlingCoordinatedOmissionMitigation> for SwanlingAttack {
3943    fn set_default(
3944        mut self,
3945        key: SwanlingDefault,
3946        value: SwanlingCoordinatedOmissionMitigation,
3947    ) -> Result<Box<Self>, SwanlingError> {
3948        match key {
3949            SwanlingDefault::CoordinatedOmissionMitigation => self.defaults.co_mitigation = Some(value),
3950            // Otherwise display a helpful and explicit error.
3951            SwanlingDefault::NoResetMetrics
3952            | SwanlingDefault::NoMetrics
3953            | SwanlingDefault::NoTaskMetrics
3954            | SwanlingDefault::NoErrorSummary
3955            | SwanlingDefault::NoDebugBody
3956            | SwanlingDefault::NoTelnet
3957            | SwanlingDefault::NoWebSocket
3958            | SwanlingDefault::NoAutoStart
3959            | SwanlingDefault::StatusCodes
3960            | SwanlingDefault::StickyFollow
3961            | SwanlingDefault::Manager
3962            | SwanlingDefault::NoHashCheck
3963            | SwanlingDefault::Worker => {
3964                return Err(SwanlingError::InvalidOption {
3965                    option: format!("SwanlingDefault::{:?}", key),
3966                    value: format!("{:?}", value),
3967                    detail: format!(
3968                        "set_default(SwanlingDefault::{:?}, {:?}) expected bool value, received SwanlingCoordinatedOmissionMitigation",
3969                        key, value
3970                    ),
3971                })
3972            }
3973            SwanlingDefault::Host
3974            | SwanlingDefault::SwanlingLog
3975            | SwanlingDefault::ReportFile
3976            | SwanlingDefault::RequestLog
3977            | SwanlingDefault::TaskLog
3978            | SwanlingDefault::RunningMetrics
3979            | SwanlingDefault::ErrorLog
3980            | SwanlingDefault::DebugLog
3981            | SwanlingDefault::TelnetHost
3982            | SwanlingDefault::WebSocketHost
3983            | SwanlingDefault::ManagerBindHost
3984            | SwanlingDefault::ManagerHost => {
3985                return Err(SwanlingError::InvalidOption {
3986                    option: format!("SwanlingDefault::{:?}", key),
3987                    value: format!("{:?}", value),
3988                    detail: format!(
3989                        "set_default(SwanlingDefault::{:?}, {:?}) expected &str value, received SwanlingCoordinatedOmissionMitigation",
3990                        key, value
3991                    ),
3992                })
3993            }
3994            SwanlingDefault::Users
3995            | SwanlingDefault::HatchRate
3996            | SwanlingDefault::RunTime
3997            | SwanlingDefault::LogLevel
3998            | SwanlingDefault::Verbose
3999            | SwanlingDefault::ThrottleRequests
4000            | SwanlingDefault::ExpectWorkers
4001            | SwanlingDefault::TelnetPort
4002            | SwanlingDefault::WebSocketPort
4003            | SwanlingDefault::ManagerBindPort
4004            | SwanlingDefault::ManagerPort => {
4005                return Err(SwanlingError::InvalidOption {
4006                    option: format!("SwanlingDefault::{:?}", key),
4007                    value: format!("{:?}", value),
4008                    detail: format!(
4009                        "set_default(SwanlingDefault::{:?}, {:?}) expected usize value, received SwanlingCoordinatedOmissionMitigation",
4010                        key, value
4011                    ),
4012                })
4013            }
4014            SwanlingDefault::RequestFormat
4015            | SwanlingDefault::DebugFormat
4016            | SwanlingDefault::ErrorFormat
4017            | SwanlingDefault::TaskFormat => {
4018                return Err(SwanlingError::InvalidOption {
4019                    option: format!("SwanlingDefault::{:?}", key),
4020                    value: format!("{:?}", value),
4021                    detail: format!(
4022                        "set_default(SwanlingDefault::{:?}, {:?}) expected SwanlingLogFormat value, received SwanlingCoordinatedOmissionMitigation",
4023                        key, value
4024                    ),
4025                })
4026            }
4027        }
4028        Ok(Box::new(self))
4029    }
4030}
4031impl SwanlingDefaultType<SwanlingLogFormat> for SwanlingAttack {
4032    fn set_default(
4033        mut self,
4034        key: SwanlingDefault,
4035        value: SwanlingLogFormat,
4036    ) -> Result<Box<Self>, SwanlingError> {
4037        match key {
4038            SwanlingDefault::RequestFormat => self.defaults.request_format = Some(value),
4039            SwanlingDefault::DebugFormat => self.defaults.debug_format = Some(value),
4040            SwanlingDefault::ErrorFormat => self.defaults.error_format = Some(value),
4041            SwanlingDefault::TaskFormat => self.defaults.task_format = Some(value),
4042            // Otherwise display a helpful and explicit error.
4043            SwanlingDefault::NoResetMetrics
4044            | SwanlingDefault::NoMetrics
4045            | SwanlingDefault::NoTaskMetrics
4046            | SwanlingDefault::NoErrorSummary
4047            | SwanlingDefault::NoDebugBody
4048            | SwanlingDefault::NoTelnet
4049            | SwanlingDefault::NoWebSocket
4050            | SwanlingDefault::NoAutoStart
4051            | SwanlingDefault::StatusCodes
4052            | SwanlingDefault::StickyFollow
4053            | SwanlingDefault::Manager
4054            | SwanlingDefault::NoHashCheck
4055            | SwanlingDefault::Worker => {
4056                return Err(SwanlingError::InvalidOption {
4057                    option: format!("SwanlingDefault::{:?}", key),
4058                    value: format!("{:?}", value),
4059                    detail: format!(
4060                        "set_default(SwanlingDefault::{:?}, {:?}) expected bool value, received SwanlingCoordinatedOmissionMitigation",
4061                        key, value
4062                    ),
4063                })
4064            }
4065            SwanlingDefault::Host
4066            | SwanlingDefault::SwanlingLog
4067            | SwanlingDefault::ReportFile
4068            | SwanlingDefault::RequestLog
4069            | SwanlingDefault::TaskLog
4070            | SwanlingDefault::RunningMetrics
4071            | SwanlingDefault::ErrorLog
4072            | SwanlingDefault::DebugLog
4073            | SwanlingDefault::TelnetHost
4074            | SwanlingDefault::WebSocketHost
4075            | SwanlingDefault::ManagerBindHost
4076            | SwanlingDefault::ManagerHost => {
4077                return Err(SwanlingError::InvalidOption {
4078                    option: format!("SwanlingDefault::{:?}", key),
4079                    value: format!("{:?}", value),
4080                    detail: format!(
4081                        "set_default(SwanlingDefault::{:?}, {:?}) expected &str value, received SwanlingCoordinatedOmissionMitigation",
4082                        key, value
4083                    ),
4084                })
4085            }
4086            SwanlingDefault::Users
4087            | SwanlingDefault::HatchRate
4088            | SwanlingDefault::RunTime
4089            | SwanlingDefault::LogLevel
4090            | SwanlingDefault::Verbose
4091            | SwanlingDefault::ThrottleRequests
4092            | SwanlingDefault::ExpectWorkers
4093            | SwanlingDefault::TelnetPort
4094            | SwanlingDefault::WebSocketPort
4095            | SwanlingDefault::ManagerBindPort
4096            | SwanlingDefault::ManagerPort => {
4097                return Err(SwanlingError::InvalidOption {
4098                    option: format!("SwanlingDefault::{:?}", key),
4099                    value: format!("{:?}", value),
4100                    detail: format!(
4101                        "set_default(SwanlingDefault::{:?}, {:?}) expected usize value, received SwanlingCoordinatedOmissionMitigation",
4102                        key, value
4103                    ),
4104                })
4105            }
4106            SwanlingDefault::CoordinatedOmissionMitigation => {
4107                return Err(SwanlingError::InvalidOption {
4108                    option: format!("SwanlingDefault::{:?}", key),
4109                    value: format!("{:?}", value),
4110                    detail: format!(
4111                        "set_default(SwanlingDefault::{:?}, {:?}) expected SwanlingCoordinatedOmissionMitigation value, received SwanlingLogFormat",
4112                        key, value
4113                    ),
4114                })
4115
4116            }
4117        }
4118        Ok(Box::new(self))
4119    }
4120}
4121
4122/// Options available when launching a Swanling load test.
4123#[derive(Options, Debug, Clone, Serialize, Deserialize)]
4124pub struct SwanlingConfiguration {
4125    /// Displays this help
4126    #[options(short = "h")]
4127    pub help: bool,
4128    /// Prints version information
4129    #[options(short = "V")]
4130    pub version: bool,
4131    // Add a blank line after this option
4132    #[options(short = "l", help = "Lists all tasks and exits\n")]
4133    pub list: bool,
4134
4135    /// Defines host to load test (ie http://10.21.32.33)
4136    #[options(short = "H")]
4137    pub host: String,
4138    /// Sets concurrent users (default: number of CPUs)
4139    #[options(short = "u")]
4140    pub users: Option<usize>,
4141    /// Sets per-second user hatch rate (default: 1)
4142    #[options(short = "r", meta = "RATE")]
4143    pub hatch_rate: Option<String>,
4144    /// Stops after (30s, 20m, 3h, 1h30m, etc)
4145    #[options(short = "t", meta = "TIME")]
4146    pub run_time: String,
4147    /// Enables Swanling log file and sets name
4148    #[options(short = "G", meta = "NAME")]
4149    pub swanling_log: String,
4150    /// Sets Swanling log level (-g, -gg, etc)
4151    #[options(short = "g", count)]
4152    pub log_level: u8,
4153    #[options(
4154        count,
4155        short = "v",
4156        // Add a blank line and then a 'Metrics:' header after this option
4157        help = "Sets Swanling verbosity (-v, -vv, etc)\n\nMetrics:"
4158    )]
4159    pub verbose: u8,
4160
4161    /// How often to optionally print running metrics
4162    #[options(no_short, meta = "TIME")]
4163    pub running_metrics: Option<usize>,
4164    /// Doesn't reset metrics after all users have started
4165    #[options(no_short)]
4166    pub no_reset_metrics: bool,
4167    /// Doesn't track metrics
4168    #[options(no_short)]
4169    pub no_metrics: bool,
4170    /// Doesn't track task metrics
4171    #[options(no_short)]
4172    pub no_task_metrics: bool,
4173    /// Doesn't display an error summary
4174    #[options(no_short)]
4175    pub no_error_summary: bool,
4176    /// Create an html-formatted report
4177    #[options(no_short, meta = "NAME")]
4178    pub report_file: String,
4179    /// Sets request log file name
4180    #[options(short = "R", meta = "NAME")]
4181    pub request_log: String,
4182    /// Sets request log format (csv, json, raw)
4183    #[options(no_short, meta = "FORMAT")]
4184    pub request_format: Option<SwanlingLogFormat>,
4185    /// Sets task log file name
4186    #[options(short = "T", meta = "NAME")]
4187    pub task_log: String,
4188    /// Sets task log format (csv, json, raw)
4189    #[options(no_short, meta = "FORMAT")]
4190    pub task_format: Option<SwanlingLogFormat>,
4191    /// Sets error log file name
4192    #[options(short = "E", meta = "NAME")]
4193    pub error_log: String,
4194    /// Sets error log format (csv, json, raw)
4195    #[options(no_short, meta = "FORMAT")]
4196    pub error_format: Option<SwanlingLogFormat>,
4197    /// Sets debug log file name
4198    #[options(short = "D", meta = "NAME")]
4199    pub debug_log: String,
4200    /// Sets debug log format (csv, json, raw)
4201    #[options(no_short, meta = "FORMAT")]
4202    pub debug_format: Option<SwanlingLogFormat>,
4203    /// Do not include the response body in the debug log
4204    #[options(no_short)]
4205    pub no_debug_body: bool,
4206    // Add a blank line and then an Advanced: header after this option
4207    #[options(no_short, help = "Tracks additional status code metrics\n\nAdvanced:")]
4208    pub status_codes: bool,
4209
4210    /// Doesn't enable telnet Controller
4211    #[options(no_short)]
4212    pub no_telnet: bool,
4213    /// Sets telnet Controller host (default: 0.0.0.0)
4214    #[options(no_short, meta = "HOST")]
4215    pub telnet_host: String,
4216    /// Sets telnet Controller TCP port (default: 5116)
4217    #[options(no_short, meta = "PORT")]
4218    pub telnet_port: u16,
4219    /// Doesn't enable WebSocket Controller
4220    #[options(no_short)]
4221    pub no_websocket: bool,
4222    /// Sets WebSocket Controller host (default: 0.0.0.0)
4223    #[options(no_short, meta = "HOST")]
4224    pub websocket_host: String,
4225    /// Sets WebSocket Controller TCP port (default: 5117)
4226    #[options(no_short, meta = "PORT")]
4227    pub websocket_port: u16,
4228    /// Doesn't automatically start load test
4229    #[options(no_short)]
4230    pub no_autostart: bool,
4231    /// Sets coordinated omission mitigation strategy
4232    #[options(no_short, meta = "STRATEGY")]
4233    pub co_mitigation: Option<SwanlingCoordinatedOmissionMitigation>,
4234    /// Sets maximum requests per second
4235    #[options(no_short, meta = "VALUE")]
4236    pub throttle_requests: usize,
4237    #[options(
4238        no_short,
4239        help = "Follows base_url redirect with subsequent requests\n\nGaggle:"
4240    )]
4241    pub sticky_follow: bool,
4242
4243    /// Enables distributed load test Manager mode
4244    #[options(no_short)]
4245    pub manager: bool,
4246    /// Sets number of Workers to expect
4247    #[options(no_short, meta = "VALUE")]
4248    pub expect_workers: Option<u16>,
4249    /// Tells Manager to ignore load test checksum
4250    #[options(no_short)]
4251    pub no_hash_check: bool,
4252    /// Sets host Manager listens on (default: 0.0.0.0)
4253    #[options(no_short, meta = "HOST")]
4254    pub manager_bind_host: String,
4255    /// Sets port Manager listens on (default: 5115)
4256    #[options(no_short, meta = "PORT")]
4257    pub manager_bind_port: u16,
4258    /// Enables distributed load test Worker mode
4259    #[options(no_short)]
4260    pub worker: bool,
4261    /// Sets host Worker connects to (default: 127.0.0.1)
4262    #[options(no_short, meta = "HOST")]
4263    pub manager_host: String,
4264    /// Sets port Worker connects to (default: 5115)
4265    #[options(no_short, meta = "PORT")]
4266    pub manager_port: u16,
4267}
4268
4269/// Use the configured SwanlingScheduler to allocate all [`SwanlingTask`](./swanling/struct.SwanlingTask.html)s
4270/// within the [`SwanlingTaskSet`](./swanling/struct.SwanlingTaskSet.html) in the appropriate order. Returns
4271/// three set of ordered tasks: /// `on_start_tasks`, `tasks`, and `on_stop_tasks`. The
4272/// `on_start_tasks` are only run once when the [`SwanlingAttack`](./struct.SwanlingAttack.html) first
4273/// starts. Normal `tasks` are then run for the duration of the
4274/// [`SwanlingAttack`](./struct.SwanlingAttack.html). The `on_stop_tasks` finally are only run once when
4275/// the [`SwanlingAttack`](./struct.SwanlingAttack.html) stops.
4276fn allocate_tasks(
4277    task_set: &SwanlingTaskSet,
4278    scheduler: &SwanlingScheduler,
4279) -> (
4280    WeightedSwanlingTasks,
4281    WeightedSwanlingTasks,
4282    WeightedSwanlingTasks,
4283) {
4284    debug!(
4285        "allocating SwanlingTasks on SwanlingUsers with {:?} scheduler",
4286        scheduler
4287    );
4288
4289    // A BTreeMap of Vectors allows us to group and sort tasks per sequence value.
4290    let mut sequenced_tasks: SequencedSwanlingTasks = BTreeMap::new();
4291    let mut sequenced_on_start_tasks: SequencedSwanlingTasks = BTreeMap::new();
4292    let mut sequenced_on_stop_tasks: SequencedSwanlingTasks = BTreeMap::new();
4293    let mut unsequenced_tasks: UnsequencedSwanlingTasks = Vec::new();
4294    let mut unsequenced_on_start_tasks: UnsequencedSwanlingTasks = Vec::new();
4295    let mut unsequenced_on_stop_tasks: UnsequencedSwanlingTasks = Vec::new();
4296    let mut u: usize = 0;
4297    let mut v: usize;
4298
4299    // Find the greatest common divisor of all tasks in the task_set.
4300    for task in &task_set.tasks {
4301        if task.sequence > 0 {
4302            if task.on_start {
4303                if let Some(sequence) = sequenced_on_start_tasks.get_mut(&task.sequence) {
4304                    // This is another task with this order value.
4305                    sequence.push(task.clone());
4306                } else {
4307                    // This is the first task with this order value.
4308                    sequenced_on_start_tasks.insert(task.sequence, vec![task.clone()]);
4309                }
4310            }
4311            // Allow a task to be both on_start and on_stop.
4312            if task.on_stop {
4313                if let Some(sequence) = sequenced_on_stop_tasks.get_mut(&task.sequence) {
4314                    // This is another task with this order value.
4315                    sequence.push(task.clone());
4316                } else {
4317                    // This is the first task with this order value.
4318                    sequenced_on_stop_tasks.insert(task.sequence, vec![task.clone()]);
4319                }
4320            }
4321            if !task.on_start && !task.on_stop {
4322                if let Some(sequence) = sequenced_tasks.get_mut(&task.sequence) {
4323                    // This is another task with this order value.
4324                    sequence.push(task.clone());
4325                } else {
4326                    // This is the first task with this order value.
4327                    sequenced_tasks.insert(task.sequence, vec![task.clone()]);
4328                }
4329            }
4330        } else {
4331            if task.on_start {
4332                unsequenced_on_start_tasks.push(task.clone());
4333            }
4334            if task.on_stop {
4335                unsequenced_on_stop_tasks.push(task.clone());
4336            }
4337            if !task.on_start && !task.on_stop {
4338                unsequenced_tasks.push(task.clone());
4339            }
4340        }
4341        // Look for lowest common divisor amongst all tasks of any weight.
4342        if u == 0 {
4343            u = task.weight;
4344        } else {
4345            v = task.weight;
4346            trace!("calculating greatest common denominator of {} and {}", u, v);
4347            u = util::gcd(u, v);
4348            trace!("inner gcd: {}", u);
4349        }
4350    }
4351    // 'u' will always be the greatest common divisor
4352    debug!("gcd: {}", u);
4353
4354    // Apply weights to sequenced tasks.
4355    let weighted_sequenced_on_start_tasks = weight_sequenced_tasks(&sequenced_on_start_tasks, u);
4356    let weighted_sequenced_tasks = weight_sequenced_tasks(&sequenced_tasks, u);
4357    let weighted_sequenced_on_stop_tasks = weight_sequenced_tasks(&sequenced_on_stop_tasks, u);
4358
4359    // Apply weights to unsequenced tasks.
4360    let (weighted_unsequenced_on_start_tasks, total_unsequenced_on_start_tasks) =
4361        weight_unsequenced_tasks(&unsequenced_on_start_tasks, u);
4362    let (weighted_unsequenced_tasks, total_unsequenced_tasks) =
4363        weight_unsequenced_tasks(&unsequenced_tasks, u);
4364    let (weighted_unsequenced_on_stop_tasks, total_unsequenced_on_stop_tasks) =
4365        weight_unsequenced_tasks(&unsequenced_on_stop_tasks, u);
4366
4367    // Schedule sequenced tasks.
4368    let scheduled_sequenced_on_start_tasks =
4369        schedule_sequenced_tasks(&weighted_sequenced_on_start_tasks, scheduler);
4370    let scheduled_sequenced_tasks = schedule_sequenced_tasks(&weighted_sequenced_tasks, scheduler);
4371    let scheduled_sequenced_on_stop_tasks =
4372        schedule_sequenced_tasks(&weighted_sequenced_on_stop_tasks, scheduler);
4373
4374    // Schedule unsequenced tasks.
4375    let scheduled_unsequenced_on_start_tasks = schedule_unsequenced_tasks(
4376        &weighted_unsequenced_on_start_tasks,
4377        total_unsequenced_on_start_tasks,
4378        scheduler,
4379    );
4380    let scheduled_unsequenced_tasks = schedule_unsequenced_tasks(
4381        &weighted_unsequenced_tasks,
4382        total_unsequenced_tasks,
4383        scheduler,
4384    );
4385    let scheduled_unsequenced_on_stop_tasks = schedule_unsequenced_tasks(
4386        &weighted_unsequenced_on_stop_tasks,
4387        total_unsequenced_on_stop_tasks,
4388        scheduler,
4389    );
4390
4391    // Finally build a Vector of tuples: (task id, task name)
4392    let mut on_start_tasks = Vec::new();
4393    let mut tasks = Vec::new();
4394    let mut on_stop_tasks = Vec::new();
4395
4396    // Sequenced tasks come first.
4397    for task in scheduled_sequenced_on_start_tasks.iter() {
4398        on_start_tasks.extend(vec![(*task, task_set.tasks[*task].name.to_string())])
4399    }
4400    for task in scheduled_sequenced_tasks.iter() {
4401        tasks.extend(vec![(*task, task_set.tasks[*task].name.to_string())])
4402    }
4403    for task in scheduled_sequenced_on_stop_tasks.iter() {
4404        on_stop_tasks.extend(vec![(*task, task_set.tasks[*task].name.to_string())])
4405    }
4406
4407    // Unsequenced tasks come last.
4408    for task in scheduled_unsequenced_on_start_tasks.iter() {
4409        on_start_tasks.extend(vec![(*task, task_set.tasks[*task].name.to_string())])
4410    }
4411    for task in scheduled_unsequenced_tasks.iter() {
4412        tasks.extend(vec![(*task, task_set.tasks[*task].name.to_string())])
4413    }
4414    for task in scheduled_unsequenced_on_stop_tasks.iter() {
4415        on_stop_tasks.extend(vec![(*task, task_set.tasks[*task].name.to_string())])
4416    }
4417
4418    // Return sequenced buckets of weighted usize pointers to and names of Swanling Tasks
4419    (on_start_tasks, tasks, on_stop_tasks)
4420}
4421
4422/// Build a weighted vector of vectors of unsequenced SwanlingTasks.
4423fn weight_unsequenced_tasks(
4424    unsequenced_tasks: &[SwanlingTask],
4425    u: usize,
4426) -> (Vec<Vec<usize>>, usize) {
4427    // Build a vector of vectors to be used to schedule users.
4428    let mut available_unsequenced_tasks = Vec::with_capacity(unsequenced_tasks.len());
4429    let mut total_tasks = 0;
4430    for task in unsequenced_tasks.iter() {
4431        // divide by greatest common divisor so vector is as short as possible
4432        let weight = task.weight / u;
4433        trace!(
4434            "{}: {} has weight of {} (reduced with gcd to {})",
4435            task.tasks_index,
4436            task.name,
4437            task.weight,
4438            weight
4439        );
4440        let weighted_tasks = vec![task.tasks_index; weight];
4441        available_unsequenced_tasks.push(weighted_tasks);
4442        total_tasks += weight;
4443    }
4444    (available_unsequenced_tasks, total_tasks)
4445}
4446
4447/// Build a weighted vector of vectors of sequenced SwanlingTasks.
4448fn weight_sequenced_tasks(
4449    sequenced_tasks: &SequencedSwanlingTasks,
4450    u: usize,
4451) -> BTreeMap<usize, Vec<Vec<usize>>> {
4452    // Build a sequenced BTreeMap containing weighted vectors of SwanlingTasks.
4453    let mut available_sequenced_tasks = BTreeMap::new();
4454    // Step through sequences, each containing a bucket of all SwanlingTasks with the same
4455    // sequence value, allowing actual weighting to be done by weight_unsequenced_tasks().
4456    for (sequence, unsequenced_tasks) in sequenced_tasks.iter() {
4457        let (weighted_tasks, _total_weighted_tasks) =
4458            weight_unsequenced_tasks(&unsequenced_tasks, u);
4459        available_sequenced_tasks.insert(*sequence, weighted_tasks);
4460    }
4461
4462    available_sequenced_tasks
4463}
4464
4465fn schedule_sequenced_tasks(
4466    available_sequenced_tasks: &BTreeMap<usize, Vec<Vec<usize>>>,
4467    scheduler: &SwanlingScheduler,
4468) -> Vec<usize> {
4469    let mut weighted_tasks: Vec<usize> = Vec::new();
4470
4471    for (_sequence, tasks) in available_sequenced_tasks.iter() {
4472        let scheduled_tasks = schedule_unsequenced_tasks(tasks, tasks[0].len(), scheduler);
4473        weighted_tasks.extend(scheduled_tasks);
4474    }
4475
4476    weighted_tasks
4477}
4478
4479// Return a list of tasks in the order to be run.
4480fn schedule_unsequenced_tasks(
4481    available_unsequenced_tasks: &[Vec<usize>],
4482    total_tasks: usize,
4483    scheduler: &SwanlingScheduler,
4484) -> Vec<usize> {
4485    // Now build the weighted list with the appropriate scheduler.
4486    let mut weighted_tasks = Vec::new();
4487
4488    match scheduler {
4489        SwanlingScheduler::RoundRobin => {
4490            // Allocate task sets round robin.
4491            let tasks_len = available_unsequenced_tasks.len();
4492            let mut available_tasks = available_unsequenced_tasks.to_owned();
4493            loop {
4494                // Tasks are contained in a vector of vectors. The outer vectors each
4495                // contain a different SwanlingTask, and the inner vectors contain each
4496                // instance of that specific SwanlingTask.
4497                for (task_index, tasks) in available_tasks.iter_mut().enumerate().take(tasks_len) {
4498                    if let Some(task) = tasks.pop() {
4499                        debug!("allocating task from Task {}", task_index);
4500                        weighted_tasks.push(task);
4501                    }
4502                }
4503                if weighted_tasks.len() >= total_tasks {
4504                    break;
4505                }
4506            }
4507        }
4508        SwanlingScheduler::Serial | SwanlingScheduler::Random => {
4509            // Allocate task sets serially in the weighted order defined. If the Random
4510            // scheduler is being used, tasks will get shuffled later.
4511            for (task_index, tasks) in available_unsequenced_tasks.iter().enumerate() {
4512                debug!(
4513                    "allocating all {} tasks from Task {}",
4514                    tasks.len(),
4515                    task_index
4516                );
4517
4518                let mut tasks_clone = tasks.clone();
4519                if scheduler == &SwanlingScheduler::Random {
4520                    tasks_clone.shuffle(&mut thread_rng());
4521                }
4522                weighted_tasks.append(&mut tasks_clone);
4523            }
4524        }
4525    }
4526
4527    weighted_tasks
4528}
4529
4530#[cfg(test)]
4531mod test {
4532    use super::*;
4533
4534    #[test]
4535    fn set_defaults() {
4536        let host = "http://example.com/".to_string();
4537        let users: usize = 10;
4538        let run_time: usize = 10;
4539        let hatch_rate = "2".to_string();
4540        let log_level: usize = 1;
4541        let swanling_log = "custom-swanling.log".to_string();
4542        let verbose: usize = 0;
4543        let report_file = "custom-swanling-report.html".to_string();
4544        let request_log = "custom-swanling-request.log".to_string();
4545        let task_log = "custom-swanling-task.log".to_string();
4546        let debug_log = "custom-swanling-debug.log".to_string();
4547        let error_log = "custom-swanling-error.log".to_string();
4548        let throttle_requests: usize = 25;
4549        let expect_workers: usize = 5;
4550        let manager_bind_host = "127.0.0.1".to_string();
4551        let manager_bind_port: usize = 1221;
4552        let manager_host = "127.0.0.1".to_string();
4553        let manager_port: usize = 1221;
4554
4555        let swanling_attack = SwanlingAttack::initialize()
4556            .unwrap()
4557            .set_default(SwanlingDefault::Host, host.as_str())
4558            .unwrap()
4559            .set_default(SwanlingDefault::Users, users)
4560            .unwrap()
4561            .set_default(SwanlingDefault::RunTime, run_time)
4562            .unwrap()
4563            .set_default(SwanlingDefault::HatchRate, hatch_rate.as_str())
4564            .unwrap()
4565            .set_default(SwanlingDefault::LogLevel, log_level)
4566            .unwrap()
4567            .set_default(SwanlingDefault::SwanlingLog, swanling_log.as_str())
4568            .unwrap()
4569            .set_default(SwanlingDefault::Verbose, verbose)
4570            .unwrap()
4571            .set_default(SwanlingDefault::RunningMetrics, 15)
4572            .unwrap()
4573            .set_default(SwanlingDefault::NoResetMetrics, true)
4574            .unwrap()
4575            .set_default(SwanlingDefault::NoMetrics, true)
4576            .unwrap()
4577            .set_default(SwanlingDefault::NoTaskMetrics, true)
4578            .unwrap()
4579            .set_default(SwanlingDefault::NoErrorSummary, true)
4580            .unwrap()
4581            .set_default(SwanlingDefault::NoTelnet, true)
4582            .unwrap()
4583            .set_default(SwanlingDefault::NoWebSocket, true)
4584            .unwrap()
4585            .set_default(SwanlingDefault::NoAutoStart, true)
4586            .unwrap()
4587            .set_default(SwanlingDefault::ReportFile, report_file.as_str())
4588            .unwrap()
4589            .set_default(SwanlingDefault::RequestLog, request_log.as_str())
4590            .unwrap()
4591            .set_default(SwanlingDefault::RequestFormat, SwanlingLogFormat::Raw)
4592            .unwrap()
4593            .set_default(SwanlingDefault::TaskLog, task_log.as_str())
4594            .unwrap()
4595            .set_default(SwanlingDefault::TaskFormat, SwanlingLogFormat::Raw)
4596            .unwrap()
4597            .set_default(SwanlingDefault::ErrorLog, error_log.as_str())
4598            .unwrap()
4599            .set_default(SwanlingDefault::ErrorFormat, SwanlingLogFormat::Csv)
4600            .unwrap()
4601            .set_default(SwanlingDefault::DebugLog, debug_log.as_str())
4602            .unwrap()
4603            .set_default(SwanlingDefault::DebugFormat, SwanlingLogFormat::Csv)
4604            .unwrap()
4605            .set_default(SwanlingDefault::NoDebugBody, true)
4606            .unwrap()
4607            .set_default(SwanlingDefault::StatusCodes, true)
4608            .unwrap()
4609            .set_default(
4610                SwanlingDefault::CoordinatedOmissionMitigation,
4611                SwanlingCoordinatedOmissionMitigation::Disabled,
4612            )
4613            .unwrap()
4614            .set_default(SwanlingDefault::ThrottleRequests, throttle_requests)
4615            .unwrap()
4616            .set_default(SwanlingDefault::StickyFollow, true)
4617            .unwrap()
4618            .set_default(SwanlingDefault::Manager, true)
4619            .unwrap()
4620            .set_default(SwanlingDefault::ExpectWorkers, expect_workers)
4621            .unwrap()
4622            .set_default(SwanlingDefault::NoHashCheck, true)
4623            .unwrap()
4624            .set_default(SwanlingDefault::ManagerBindHost, manager_bind_host.as_str())
4625            .unwrap()
4626            .set_default(SwanlingDefault::ManagerBindPort, manager_bind_port)
4627            .unwrap()
4628            .set_default(SwanlingDefault::Worker, true)
4629            .unwrap()
4630            .set_default(SwanlingDefault::ManagerHost, manager_host.as_str())
4631            .unwrap()
4632            .set_default(SwanlingDefault::ManagerPort, manager_port)
4633            .unwrap();
4634
4635        assert!(swanling_attack.defaults.host == Some(host));
4636        assert!(swanling_attack.defaults.users == Some(users));
4637        assert!(swanling_attack.defaults.run_time == Some(run_time));
4638        assert!(swanling_attack.defaults.hatch_rate == Some(hatch_rate));
4639        assert!(swanling_attack.defaults.log_level == Some(log_level as u8));
4640        assert!(swanling_attack.defaults.swanling_log == Some(swanling_log));
4641        assert!(swanling_attack.defaults.no_debug_body == Some(true));
4642        assert!(swanling_attack.defaults.verbose == Some(verbose as u8));
4643        assert!(swanling_attack.defaults.running_metrics == Some(15));
4644        assert!(swanling_attack.defaults.no_reset_metrics == Some(true));
4645        assert!(swanling_attack.defaults.no_metrics == Some(true));
4646        assert!(swanling_attack.defaults.no_task_metrics == Some(true));
4647        assert!(swanling_attack.defaults.no_error_summary == Some(true));
4648        assert!(swanling_attack.defaults.no_telnet == Some(true));
4649        assert!(swanling_attack.defaults.no_websocket == Some(true));
4650        assert!(swanling_attack.defaults.no_autostart == Some(true));
4651        assert!(swanling_attack.defaults.report_file == Some(report_file));
4652        assert!(swanling_attack.defaults.request_log == Some(request_log));
4653        assert!(swanling_attack.defaults.request_format == Some(SwanlingLogFormat::Raw));
4654        assert!(swanling_attack.defaults.error_log == Some(error_log));
4655        assert!(swanling_attack.defaults.error_format == Some(SwanlingLogFormat::Csv));
4656        assert!(swanling_attack.defaults.debug_log == Some(debug_log));
4657        assert!(swanling_attack.defaults.debug_format == Some(SwanlingLogFormat::Csv));
4658        assert!(swanling_attack.defaults.status_codes == Some(true));
4659        assert!(
4660            swanling_attack.defaults.co_mitigation
4661                == Some(SwanlingCoordinatedOmissionMitigation::Disabled)
4662        );
4663        assert!(swanling_attack.defaults.throttle_requests == Some(throttle_requests));
4664        assert!(swanling_attack.defaults.sticky_follow == Some(true));
4665        assert!(swanling_attack.defaults.manager == Some(true));
4666        assert!(swanling_attack.defaults.expect_workers == Some(expect_workers as u16));
4667        assert!(swanling_attack.defaults.no_hash_check == Some(true));
4668        assert!(swanling_attack.defaults.manager_bind_host == Some(manager_bind_host));
4669        assert!(swanling_attack.defaults.manager_bind_port == Some(manager_bind_port as u16));
4670        assert!(swanling_attack.defaults.worker == Some(true));
4671        assert!(swanling_attack.defaults.manager_host == Some(manager_host));
4672        assert!(swanling_attack.defaults.manager_port == Some(manager_port as u16));
4673    }
4674}