1use gumdrop::Options;
9use serde::{Deserialize, Serialize};
10use simplelog::*;
11use std::path::PathBuf;
12use std::str::FromStr;
13
14use crate::logger::GooseLogFormat;
15use crate::metrics::GooseCoordinatedOmissionMitigation;
16use crate::test_plan::TestPlan;
17use crate::util;
18use crate::{GooseAttack, GooseError};
19
20#[derive(Options, Debug, Clone, Default, Serialize, Deserialize)]
31#[options(
32 help = r#"Goose is a modern, high-performance, distributed HTTP(S) load testing tool,
33written in Rust. Visit https://book.goose.rs/ for more information.
34
35The following runtime options are available when launching a Goose load test:"#
36)]
37pub struct GooseConfiguration {
38 #[options(short = "h")]
40 pub help: bool,
41 #[options(short = "V")]
43 pub version: bool,
44 #[options(short = "l", help = "Lists all transactions and exits\n")]
47 pub list: bool,
48
49 #[options(short = "H")]
51 pub host: String,
52 #[options(short = "u")]
54 pub users: Option<usize>,
55 #[options(short = "r", meta = "RATE")]
57 pub hatch_rate: Option<String>,
58 #[options(short = "s", meta = "TIME")]
60 pub startup_time: String,
61 #[options(short = "t", meta = "TIME")]
63 pub run_time: String,
64 #[options(short = "G", meta = "NAME")]
66 pub goose_log: String,
67 #[options(short = "g", count)]
69 pub log_level: u8,
70 #[options(count, short = "q", help = "Decreases Goose verbosity (-q, -qq, etc)")]
72 pub quiet: u8,
73 #[options(
75 count,
76 short = "v",
77 help = "Increases Goose verbosity (-v, -vv, etc)\n\nMetrics:"
79 )]
80 pub verbose: u8,
81
82 #[options(no_short, meta = "TIME")]
84 pub running_metrics: Option<usize>,
85 #[options(no_short)]
87 pub no_reset_metrics: bool,
88 #[options(no_short)]
90 pub no_metrics: bool,
91 #[options(no_short)]
93 pub no_transaction_metrics: bool,
94 #[options(no_short)]
96 pub no_scenario_metrics: bool,
97 #[options(no_short)]
99 pub no_print_metrics: bool,
100 #[options(no_short)]
102 pub no_error_summary: bool,
103 #[options(no_short, meta = "NAME")]
105 pub report_file: Vec<String>,
106 #[options(no_short)]
108 pub no_granular_report: bool,
109 #[options(short = "R", meta = "NAME")]
111 pub request_log: String,
112 #[options(no_short, meta = "FORMAT")]
114 pub request_format: Option<GooseLogFormat>,
115 #[options(no_short)]
117 pub request_body: bool,
118 #[options(short = "T", meta = "NAME")]
120 pub transaction_log: String,
121 #[options(no_short, meta = "FORMAT")]
123 pub transaction_format: Option<GooseLogFormat>,
124 #[options(short = "S", meta = "NAME")]
126 pub scenario_log: String,
127 #[options(no_short, meta = "FORMAT")]
129 pub scenario_format: Option<GooseLogFormat>,
130 #[options(short = "E", meta = "NAME")]
132 pub error_log: String,
133 #[options(no_short, meta = "FORMAT")]
135 pub error_format: Option<GooseLogFormat>,
136 #[options(short = "D", meta = "NAME")]
138 pub debug_log: String,
139 #[options(no_short, meta = "FORMAT")]
141 pub debug_format: Option<GooseLogFormat>,
142 #[options(no_short)]
144 pub no_debug_body: bool,
145 #[options(no_short, help = "Do not track status code metrics\n\nAdvanced:")]
148 pub no_status_codes: bool,
149
150 #[options(no_short, meta = "\"TESTPLAN\"")]
152 pub(crate) test_plan: Option<TestPlan>,
153 #[options(no_short)]
155 pub iterations: usize,
156 #[options(no_short, meta = "\"SCENARIO\"")]
158 pub scenarios: Scenarios,
159 #[options(no_short)]
161 pub scenarios_list: bool,
162 #[options(no_short)]
164 pub no_telnet: bool,
165 #[options(no_short, meta = "HOST")]
167 pub telnet_host: String,
168 #[options(no_short, meta = "PORT")]
170 pub telnet_port: u16,
171 #[options(no_short)]
173 pub no_websocket: bool,
174 #[options(no_short, meta = "HOST")]
176 pub websocket_host: String,
177 #[options(no_short, meta = "PORT")]
179 pub websocket_port: u16,
180 #[options(no_short)]
182 pub no_autostart: bool,
183 #[options(no_short)]
185 pub no_gzip: bool,
186 #[options(no_short, meta = "VALUE")]
188 pub timeout: Option<String>,
189 #[options(no_short, meta = "STRATEGY")]
191 pub co_mitigation: Option<GooseCoordinatedOmissionMitigation>,
192 #[options(no_short, meta = "VALUE")]
194 pub throttle_requests: usize,
195 #[options(no_short)]
197 pub sticky_follow: bool,
198 #[options(no_short)]
200 pub accept_invalid_certs: bool,
201}
202
203#[derive(Options, Default, Debug, Clone, Serialize, Deserialize)]
205pub struct Scenarios {
206 pub active: Vec<String>,
207}
208impl FromStr for Scenarios {
210 type Err = GooseError;
211
212 fn from_str(s: &str) -> Result<Self, Self::Err> {
213 let mut active: Vec<String> = Vec::new();
215 let lines = s.split(',');
217 for line in lines {
218 let scenario = line.trim().to_lowercase();
220 if scenario.chars().all(char::is_alphanumeric) {
222 active.push(scenario);
223 } else {
224 eprintln!("ERROR: invalid `configuration.scenarios` value: '{}'", line);
226 eprintln!(" Expected format: --scenarios \"{{one}},{{two}},{{three}}\"");
227 eprintln!(" {{one}}, {{two}}, {{three}}, etc must be alphanumeric");
228 eprintln!(" To view valid scenario names invoke `--scenarios-list`");
229 return Err(GooseError::InvalidOption {
230 option: "`configuration.scenarios".to_string(),
231 value: line.to_string(),
232 detail: "invalid `configuration.scenarios` value.".to_string(),
233 });
234 }
235 }
236 Ok(Scenarios { active })
238 }
239}
240
241#[derive(Clone, Debug, Default)]
245pub(crate) struct GooseDefaults {
246 pub host: Option<String>,
248 pub users: Option<usize>,
250 pub hatch_rate: Option<String>,
252 pub startup_time: Option<usize>,
254 pub run_time: Option<usize>,
256 pub test_plan: Option<TestPlan>,
258 pub iterations: Option<usize>,
260 pub scenarios: Option<Scenarios>,
262 pub log_level: Option<u8>,
264 pub goose_log: Option<String>,
266 pub quiet: Option<u8>,
268 pub verbose: Option<u8>,
270 pub running_metrics: Option<usize>,
272 pub no_reset_metrics: Option<bool>,
274 pub no_metrics: Option<bool>,
276 pub no_transaction_metrics: Option<bool>,
278 pub no_scenario_metrics: Option<bool>,
280 pub no_print_metrics: Option<bool>,
282 pub no_error_summary: Option<bool>,
284 pub report_file: Option<Vec<String>>,
286 pub no_granular_report: Option<bool>,
288 pub request_log: Option<String>,
290 pub request_format: Option<GooseLogFormat>,
292 pub request_body: Option<bool>,
294 pub transaction_log: Option<String>,
296 pub transaction_format: Option<GooseLogFormat>,
298 pub scenario_log: Option<String>,
300 pub scenario_format: Option<GooseLogFormat>,
302 pub error_log: Option<String>,
304 pub error_format: Option<GooseLogFormat>,
306 pub debug_log: Option<String>,
308 pub debug_format: Option<GooseLogFormat>,
310 pub no_debug_body: Option<bool>,
312 pub no_telnet: Option<bool>,
314 pub no_websocket: Option<bool>,
316 pub no_autostart: Option<bool>,
318 pub no_gzip: Option<bool>,
320 pub timeout: Option<String>,
322 pub co_mitigation: Option<GooseCoordinatedOmissionMitigation>,
324 pub no_status_codes: Option<bool>,
326 pub throttle_requests: Option<usize>,
328 pub sticky_follow: Option<bool>,
330 pub telnet_host: Option<String>,
332 pub telnet_port: Option<u16>,
334 pub websocket_host: Option<String>,
336 pub websocket_port: Option<u16>,
338 pub accept_invalid_certs: Option<bool>,
340}
341
342#[derive(Debug)]
347pub enum GooseDefault {
348 Host,
350 Users,
352 HatchRate,
354 StartupTime,
356 RunTime,
358 TestPlan,
360 Iterations,
362 Scenarios,
364 LogLevel,
366 GooseLog,
368 Quiet,
370 Verbose,
372 RunningMetrics,
374 NoResetMetrics,
376 NoMetrics,
378 NoTransactionMetrics,
380 NoScenarioMetrics,
382 NoPrintMetrics,
384 NoErrorSummary,
386 ReportFile,
388 NoGranularData,
390 RequestLog,
392 RequestFormat,
394 RequestBody,
396 TransactionLog,
398 TransactionFormat,
400 ScenarioLog,
402 ScenarioFormat,
404 ErrorLog,
406 ErrorFormat,
408 DebugLog,
410 DebugFormat,
412 NoDebugBody,
414 NoTelnet,
416 NoWebSocket,
418 CoordinatedOmissionMitigation,
420 NoAutoStart,
422 Timeout,
424 NoGzip,
426 NoStatusCodes,
428 ThrottleRequests,
430 StickyFollow,
432 TelnetHost,
434 TelnetPort,
436 WebSocketHost,
438 WebSocketPort,
440 AcceptInvalidCerts,
442}
443
444pub trait GooseDefaultType<T> {
534 fn set_default(self, key: GooseDefault, value: T) -> Result<Box<Self>, GooseError>;
555}
556impl GooseDefaultType<&str> for GooseAttack {
557 fn set_default(mut self, key: GooseDefault, value: &str) -> Result<Box<Self>, GooseError> {
559 match key {
560 GooseDefault::DebugLog => self.defaults.debug_log = Some(value.to_string()),
562 GooseDefault::ErrorLog => self.defaults.error_log = Some(value.to_string()),
563 GooseDefault::GooseLog => self.defaults.goose_log = Some(value.to_string()),
564 GooseDefault::HatchRate => self.defaults.hatch_rate = Some(value.to_string()),
565 GooseDefault::Host => {
566 self.defaults.host = if value.is_empty() {
567 None
568 } else {
569 Some(value.to_string())
570 }
571 }
572 GooseDefault::ReportFile => self.defaults.report_file = Some(vec![value.to_string()]),
573 GooseDefault::RequestLog => self.defaults.request_log = Some(value.to_string()),
574 GooseDefault::ScenarioLog => self.defaults.scenario_log = Some(value.to_string()),
575 GooseDefault::Scenarios => {
576 self.defaults.scenarios = Some(value.parse::<Scenarios>().unwrap())
577 }
578 GooseDefault::TelnetHost => self.defaults.telnet_host = Some(value.to_string()),
579 GooseDefault::TestPlan => {
580 self.defaults.test_plan = Some(value.parse::<TestPlan>().unwrap())
581 }
582 GooseDefault::Timeout => self.defaults.timeout = Some(value.to_string()),
583 GooseDefault::TransactionLog => self.defaults.transaction_log = Some(value.to_string()),
584 GooseDefault::WebSocketHost => self.defaults.websocket_host = Some(value.to_string()),
585 GooseDefault::Users
587 | GooseDefault::StartupTime
588 | GooseDefault::RunTime
589 | GooseDefault::Iterations
590 | GooseDefault::LogLevel
591 | GooseDefault::Quiet
592 | GooseDefault::Verbose
593 | GooseDefault::ThrottleRequests
594 | GooseDefault::TelnetPort
595 | GooseDefault::WebSocketPort => {
596 return Err(GooseError::InvalidOption {
597 option: format!("GooseDefault::{:?}", key),
598 value: value.to_string(),
599 detail: format!(
600 "set_default(GooseDefault::{:?}, {}) expected usize value, received &str",
601 key, value
602 ),
603 });
604 }
605 GooseDefault::RunningMetrics
606 | GooseDefault::NoResetMetrics
607 | GooseDefault::NoMetrics
608 | GooseDefault::NoTransactionMetrics
609 | GooseDefault::NoScenarioMetrics
610 | GooseDefault::RequestBody
611 | GooseDefault::NoPrintMetrics
612 | GooseDefault::NoErrorSummary
613 | GooseDefault::NoDebugBody
614 | GooseDefault::NoTelnet
615 | GooseDefault::NoWebSocket
616 | GooseDefault::NoAutoStart
617 | GooseDefault::NoGzip
618 | GooseDefault::NoStatusCodes
619 | GooseDefault::StickyFollow
620 | GooseDefault::NoGranularData
621 | GooseDefault::AcceptInvalidCerts => {
622 return Err(GooseError::InvalidOption {
623 option: format!("GooseDefault::{:?}", key),
624 value: value.to_string(),
625 detail: format!(
626 "set_default(GooseDefault::{:?}, {}) expected bool value, received &str",
627 key, value
628 ),
629 });
630 }
631 GooseDefault::DebugFormat
632 | GooseDefault::ErrorFormat
633 | GooseDefault::TransactionFormat
634 | GooseDefault::ScenarioFormat
635 | GooseDefault::RequestFormat => {
636 return Err(GooseError::InvalidOption {
637 option: format!("GooseDefault::{:?}", key),
638 value: value.to_string(),
639 detail: format!(
640 "set_default(GooseDefault::{:?}, {}) expected GooseLogFormat value, received &str",
641 key, value
642 ),
643 });
644 }
645 GooseDefault::CoordinatedOmissionMitigation => {
646 return Err(GooseError::InvalidOption {
647 option: format!("GooseDefault::{:?}", key),
648 value: value.to_string(),
649 detail: format!(
650 "set_default(GooseDefault::{:?}, {}) expected GooseCoordinatedOmissionMitigation value, received &str",
651 key, value
652 ),
653 });
654 }
655 }
656 Ok(Box::new(self))
657 }
658}
659impl GooseDefaultType<usize> for GooseAttack {
660 fn set_default(mut self, key: GooseDefault, value: usize) -> Result<Box<Self>, GooseError> {
662 match key {
663 GooseDefault::Users => self.defaults.users = Some(value),
664 GooseDefault::StartupTime => self.defaults.startup_time = Some(value),
665 GooseDefault::RunTime => self.defaults.run_time = Some(value),
666 GooseDefault::Iterations => self.defaults.iterations = Some(value),
667 GooseDefault::RunningMetrics => self.defaults.running_metrics = Some(value),
668 GooseDefault::LogLevel => self.defaults.log_level = Some(value as u8),
669 GooseDefault::Quiet => self.defaults.quiet = Some(value as u8),
670 GooseDefault::Verbose => self.defaults.verbose = Some(value as u8),
671 GooseDefault::ThrottleRequests => self.defaults.throttle_requests = Some(value),
672 GooseDefault::TelnetPort => self.defaults.telnet_port = Some(value as u16),
673 GooseDefault::WebSocketPort => self.defaults.websocket_port = Some(value as u16),
674 GooseDefault::DebugLog
676 | GooseDefault::ErrorLog
677 | GooseDefault::GooseLog
678 | GooseDefault::HatchRate
679 | GooseDefault::Host
680 | GooseDefault::ReportFile
681 | GooseDefault::RequestLog
682 | GooseDefault::ScenarioLog
683 | GooseDefault::Scenarios
684 | GooseDefault::TelnetHost
685 | GooseDefault::TestPlan
686 | GooseDefault::Timeout
687 | GooseDefault::TransactionLog
688 | GooseDefault::WebSocketHost => {
689 return Err(GooseError::InvalidOption {
690 option: format!("GooseDefault::{:?}", key),
691 value: format!("{}", value),
692 detail: format!(
693 "set_default(GooseDefault::{:?}, {}) expected &str value, received usize",
694 key, value
695 ),
696 })
697 }
698 GooseDefault::NoResetMetrics
699 | GooseDefault::NoMetrics
700 | GooseDefault::NoTransactionMetrics
701 | GooseDefault::NoScenarioMetrics
702 | GooseDefault::RequestBody
703 | GooseDefault::NoPrintMetrics
704 | GooseDefault::NoErrorSummary
705 | GooseDefault::NoDebugBody
706 | GooseDefault::NoTelnet
707 | GooseDefault::NoWebSocket
708 | GooseDefault::NoAutoStart
709 | GooseDefault::NoGzip
710 | GooseDefault::NoStatusCodes
711 | GooseDefault::StickyFollow
712 | GooseDefault::NoGranularData
713 | GooseDefault::AcceptInvalidCerts => {
714 return Err(GooseError::InvalidOption {
715 option: format!("GooseDefault::{:?}", key),
716 value: format!("{}", value),
717 detail: format!(
718 "set_default(GooseDefault::{:?}, {}) expected bool value, received usize",
719 key, value
720 ),
721 })
722 }
723 GooseDefault::RequestFormat
724 | GooseDefault::DebugFormat
725 | GooseDefault::ErrorFormat
726 | GooseDefault::ScenarioFormat
727 | GooseDefault::TransactionFormat => {
728 return Err(GooseError::InvalidOption {
729 option: format!("GooseDefault::{:?}", key),
730 value: value.to_string(),
731 detail: format!(
732 "set_default(GooseDefault::{:?}, {}) expected GooseLogFormat value, received usize",
733 key, value
734 ),
735 });
736 }
737 GooseDefault::CoordinatedOmissionMitigation => {
738 return Err(GooseError::InvalidOption {
739 option: format!("GooseDefault::{:?}", key),
740 value: value.to_string(),
741 detail: format!(
742 "set_default(GooseDefault::{:?}, {}) expected GooseCoordinatedOmissionMitigation value, received usize",
743 key, value
744 ),
745 });
746 }
747 }
748 Ok(Box::new(self))
749 }
750}
751impl GooseDefaultType<bool> for GooseAttack {
752 fn set_default(mut self, key: GooseDefault, value: bool) -> Result<Box<Self>, GooseError> {
754 match key {
755 GooseDefault::NoResetMetrics => self.defaults.no_reset_metrics = Some(value),
756 GooseDefault::NoMetrics => self.defaults.no_metrics = Some(value),
757 GooseDefault::NoTransactionMetrics => {
758 self.defaults.no_transaction_metrics = Some(value)
759 }
760 GooseDefault::NoScenarioMetrics => self.defaults.no_scenario_metrics = Some(value),
761 GooseDefault::RequestBody => self.defaults.request_body = Some(value),
762 GooseDefault::NoPrintMetrics => self.defaults.no_print_metrics = Some(value),
763 GooseDefault::NoErrorSummary => self.defaults.no_error_summary = Some(value),
764 GooseDefault::NoDebugBody => self.defaults.no_debug_body = Some(value),
765 GooseDefault::NoTelnet => self.defaults.no_telnet = Some(value),
766 GooseDefault::NoWebSocket => self.defaults.no_websocket = Some(value),
767 GooseDefault::NoAutoStart => self.defaults.no_autostart = Some(value),
768 GooseDefault::NoGzip => self.defaults.no_gzip = Some(value),
769 GooseDefault::AcceptInvalidCerts => self.defaults.accept_invalid_certs = Some(value),
770 GooseDefault::NoStatusCodes => self.defaults.no_status_codes = Some(value),
771 GooseDefault::StickyFollow => self.defaults.sticky_follow = Some(value),
772 GooseDefault::NoGranularData => self.defaults.no_granular_report = Some(value),
773 GooseDefault::DebugLog
775 | GooseDefault::ErrorLog
776 | GooseDefault::GooseLog
777 | GooseDefault::HatchRate
778 | GooseDefault::Host
779 | GooseDefault::ReportFile
780 | GooseDefault::RequestLog
781 | GooseDefault::ScenarioLog
782 | GooseDefault::Scenarios
783 | GooseDefault::TelnetHost
784 | GooseDefault::TestPlan
785 | GooseDefault::Timeout
786 | GooseDefault::TransactionLog
787 | GooseDefault::WebSocketHost => {
788 return Err(GooseError::InvalidOption {
789 option: format!("GooseDefault::{:?}", key),
790 value: format!("{}", value),
791 detail: format!(
792 "set_default(GooseDefault::{:?}, {}) expected &str value, received bool",
793 key, value
794 ),
795 })
796 }
797 GooseDefault::Users
798 | GooseDefault::StartupTime
799 | GooseDefault::RunTime
800 | GooseDefault::RunningMetrics
801 | GooseDefault::Iterations
802 | GooseDefault::LogLevel
803 | GooseDefault::Quiet
804 | GooseDefault::Verbose
805 | GooseDefault::ThrottleRequests
806 | GooseDefault::TelnetPort
807 | GooseDefault::WebSocketPort => {
808 return Err(GooseError::InvalidOption {
809 option: format!("GooseDefault::{:?}", key),
810 value: format!("{}", value),
811 detail: format!(
812 "set_default(GooseDefault::{:?}, {}) expected usize value, received bool",
813 key, value
814 ),
815 })
816 }
817 GooseDefault::RequestFormat
818 | GooseDefault::DebugFormat
819 | GooseDefault::ErrorFormat
820 | GooseDefault::ScenarioFormat
821 | GooseDefault::TransactionFormat => {
822 return Err(GooseError::InvalidOption {
823 option: format!("GooseDefault::{:?}", key),
824 value: value.to_string(),
825 detail: format!(
826 "set_default(GooseDefault::{:?}, {}) expected GooseLogFormat value, received bool",
827 key, value
828 ),
829 });
830 }
831 GooseDefault::CoordinatedOmissionMitigation => {
832 return Err(GooseError::InvalidOption {
833 option: format!("GooseDefault::{:?}", key),
834 value: value.to_string(),
835 detail: format!(
836 "set_default(GooseDefault::{:?}, {}) expected GooseCoordinatedOmissionMitigation value, received bool",
837 key, value
838 ),
839 });
840 }
841 }
842 Ok(Box::new(self))
843 }
844}
845impl GooseDefaultType<GooseCoordinatedOmissionMitigation> for GooseAttack {
846 fn set_default(
848 mut self,
849 key: GooseDefault,
850 value: GooseCoordinatedOmissionMitigation,
851 ) -> Result<Box<Self>, GooseError> {
852 match key {
853 GooseDefault::CoordinatedOmissionMitigation => self.defaults.co_mitigation = Some(value),
854 GooseDefault::NoResetMetrics
856 | GooseDefault::NoMetrics
857 | GooseDefault::NoTransactionMetrics
858 | GooseDefault::NoScenarioMetrics
859 | GooseDefault::RequestBody
860 | GooseDefault::NoPrintMetrics
861 | GooseDefault::NoErrorSummary
862 | GooseDefault::NoDebugBody
863 | GooseDefault::NoTelnet
864 | GooseDefault::NoWebSocket
865 | GooseDefault::NoAutoStart
866 | GooseDefault::NoGzip
867 | GooseDefault::NoStatusCodes
868 | GooseDefault::StickyFollow
869 | GooseDefault::NoGranularData
870 | GooseDefault::AcceptInvalidCerts => {
871 return Err(GooseError::InvalidOption {
872 option: format!("GooseDefault::{:?}", key),
873 value: format!("{:?}", value),
874 detail: format!(
875 "set_default(GooseDefault::{:?}, {:?}) expected bool value, received GooseCoordinatedOmissionMitigation",
876 key, value
877 ),
878 })
879 }
880 GooseDefault::DebugLog
882 | GooseDefault::ErrorLog
883 | GooseDefault::GooseLog
884 | GooseDefault::HatchRate
885 | GooseDefault::Host
886 | GooseDefault::ReportFile
887 | GooseDefault::RequestLog
888 | GooseDefault::ScenarioLog
889 | GooseDefault::Scenarios
890 | GooseDefault::TelnetHost
891 | GooseDefault::TestPlan
892 | GooseDefault::Timeout
893 | GooseDefault::TransactionLog
894 | GooseDefault::WebSocketHost => {
895 return Err(GooseError::InvalidOption {
896 option: format!("GooseDefault::{:?}", key),
897 value: format!("{:?}", value),
898 detail: format!(
899 "set_default(GooseDefault::{:?}, {:?}) expected &str value, received GooseCoordinatedOmissionMitigation",
900 key, value
901 ),
902 })
903 }
904 GooseDefault::Users
905 | GooseDefault::StartupTime
906 | GooseDefault::RunTime
907 | GooseDefault::RunningMetrics
908 | GooseDefault::Iterations
909 | GooseDefault::LogLevel
910 | GooseDefault::Quiet
911 | GooseDefault::Verbose
912 | GooseDefault::ThrottleRequests
913 | GooseDefault::TelnetPort
914 | GooseDefault::WebSocketPort => {
915 return Err(GooseError::InvalidOption {
916 option: format!("GooseDefault::{:?}", key),
917 value: format!("{:?}", value),
918 detail: format!(
919 "set_default(GooseDefault::{:?}, {:?}) expected usize value, received GooseCoordinatedOmissionMitigation",
920 key, value
921 ),
922 })
923 }
924 GooseDefault::RequestFormat
925 | GooseDefault::DebugFormat
926 | GooseDefault::ErrorFormat
927 | GooseDefault::ScenarioFormat
928 | GooseDefault::TransactionFormat => {
929 return Err(GooseError::InvalidOption {
930 option: format!("GooseDefault::{:?}", key),
931 value: format!("{:?}", value),
932 detail: format!(
933 "set_default(GooseDefault::{:?}, {:?}) expected GooseLogFormat value, received GooseCoordinatedOmissionMitigation",
934 key, value
935 ),
936 })
937 }
938 }
939 Ok(Box::new(self))
940 }
941}
942impl GooseDefaultType<GooseLogFormat> for GooseAttack {
943 fn set_default(
945 mut self,
946 key: GooseDefault,
947 value: GooseLogFormat,
948 ) -> Result<Box<Self>, GooseError> {
949 match key {
950 GooseDefault::RequestFormat => self.defaults.request_format = Some(value),
951 GooseDefault::DebugFormat => self.defaults.debug_format = Some(value),
952 GooseDefault::ErrorFormat => self.defaults.error_format = Some(value),
953 GooseDefault::TransactionFormat => self.defaults.transaction_format = Some(value),
954 GooseDefault::ScenarioFormat => self.defaults.scenario_format = Some(value),
955 GooseDefault::NoResetMetrics
957 | GooseDefault::NoMetrics
958 | GooseDefault::NoTransactionMetrics
959 | GooseDefault::NoScenarioMetrics
960 | GooseDefault::RequestBody
961 | GooseDefault::NoPrintMetrics
962 | GooseDefault::NoErrorSummary
963 | GooseDefault::NoDebugBody
964 | GooseDefault::NoTelnet
965 | GooseDefault::NoWebSocket
966 | GooseDefault::NoAutoStart
967 | GooseDefault::NoGzip
968 | GooseDefault::NoStatusCodes
969 | GooseDefault::StickyFollow
970 | GooseDefault::NoGranularData
971 | GooseDefault::AcceptInvalidCerts => {
972 return Err(GooseError::InvalidOption {
973 option: format!("GooseDefault::{:?}", key),
974 value: format!("{:?}", value),
975 detail: format!(
976 "set_default(GooseDefault::{:?}, {:?}) expected bool value, received GooseCoordinatedOmissionMitigation",
977 key, value
978 ),
979 })
980 }
981 GooseDefault::DebugLog
983 | GooseDefault::ErrorLog
984 | GooseDefault::GooseLog
985 | GooseDefault::HatchRate
986 | GooseDefault::Host
987 | GooseDefault::ReportFile
988 | GooseDefault::RequestLog
989 | GooseDefault::ScenarioLog
990 | GooseDefault::Scenarios
991 | GooseDefault::TelnetHost
992 | GooseDefault::TestPlan
993 | GooseDefault::Timeout
994 | GooseDefault::TransactionLog
995 | GooseDefault::WebSocketHost => {
996 return Err(GooseError::InvalidOption {
997 option: format!("GooseDefault::{:?}", key),
998 value: format!("{:?}", value),
999 detail: format!(
1000 "set_default(GooseDefault::{:?}, {:?}) expected &str value, received GooseCoordinatedOmissionMitigation",
1001 key, value
1002 ),
1003 })
1004 }
1005 GooseDefault::Users
1006 | GooseDefault::StartupTime
1007 | GooseDefault::RunTime
1008 | GooseDefault::RunningMetrics
1009 | GooseDefault::Iterations
1010 | GooseDefault::LogLevel
1011 | GooseDefault::Quiet
1012 | GooseDefault::Verbose
1013 | GooseDefault::ThrottleRequests
1014 | GooseDefault::TelnetPort
1015 | GooseDefault::WebSocketPort => {
1016 return Err(GooseError::InvalidOption {
1017 option: format!("GooseDefault::{:?}", key),
1018 value: format!("{:?}", value),
1019 detail: format!(
1020 "set_default(GooseDefault::{:?}, {:?}) expected usize value, received GooseCoordinatedOmissionMitigation",
1021 key, value
1022 ),
1023 })
1024 }
1025 GooseDefault::CoordinatedOmissionMitigation => {
1026 return Err(GooseError::InvalidOption {
1027 option: format!("GooseDefault::{:?}", key),
1028 value: format!("{:?}", value),
1029 detail: format!(
1030 "set_default(GooseDefault::{:?}, {:?}) expected GooseCoordinatedOmissionMitigation value, received GooseLogFormat",
1031 key, value
1032 ),
1033 })
1034 }
1035 }
1036 Ok(Box::new(self))
1037 }
1038}
1039
1040#[derive(Debug, Clone)]
1042pub(crate) struct GooseValue<'a, T> {
1043 pub(crate) value: Option<T>,
1045 pub(crate) filter: bool,
1047 pub(crate) message: &'a str,
1049}
1050
1051pub(crate) trait GooseConfigure<T> {
1052 fn get_value(&self, values: Vec<GooseValue<T>>) -> Option<T>;
1054}
1055
1056impl GooseConfigure<usize> for GooseConfiguration {
1057 fn get_value(&self, values: Vec<GooseValue<usize>>) -> Option<usize> {
1059 for value in values {
1060 if let Some(v) = value.value {
1061 if value.filter {
1062 continue;
1063 } else {
1064 if !value.message.is_empty() {
1065 info!("{} = {}", value.message, v)
1066 }
1067 return Some(v);
1068 }
1069 }
1070 }
1071 None
1072 }
1073}
1074impl GooseConfigure<u16> for GooseConfiguration {
1075 fn get_value(&self, values: Vec<GooseValue<u16>>) -> Option<u16> {
1077 for value in values {
1078 if let Some(v) = value.value {
1079 if value.filter {
1080 continue;
1081 } else {
1082 if !value.message.is_empty() {
1083 info!("{} = {}", value.message, v)
1084 }
1085 return Some(v);
1086 }
1087 }
1088 }
1089 None
1090 }
1091}
1092impl GooseConfigure<u8> for GooseConfiguration {
1093 fn get_value(&self, values: Vec<GooseValue<u8>>) -> Option<u8> {
1095 for value in values {
1096 if let Some(v) = value.value {
1097 if value.filter {
1098 continue;
1099 } else {
1100 if !value.message.is_empty() {
1101 info!("{} = {}", value.message, v)
1102 }
1103 return Some(v);
1104 }
1105 }
1106 }
1107 None
1108 }
1109}
1110impl GooseConfigure<f32> for GooseConfiguration {
1111 fn get_value(&self, values: Vec<GooseValue<f32>>) -> Option<f32> {
1113 for value in values {
1114 if let Some(v) = value.value {
1115 if value.filter {
1116 continue;
1117 } else {
1118 if !value.message.is_empty() {
1119 info!("{} = {}", value.message, v)
1120 }
1121 return Some(v);
1122 }
1123 }
1124 }
1125 None
1126 }
1127}
1128impl GooseConfigure<TestPlan> for GooseConfiguration {
1129 fn get_value(&self, values: Vec<GooseValue<TestPlan>>) -> Option<TestPlan> {
1131 for value in values {
1132 if let Some(v) = value.value {
1133 if value.filter {
1134 continue;
1135 } else {
1136 if !value.message.is_empty() {
1137 info!("{} = {:?}", value.message, v)
1138 }
1139 return Some(v);
1140 }
1141 }
1142 }
1143 None
1144 }
1145}
1146impl GooseConfigure<String> for GooseConfiguration {
1147 fn get_value(&self, values: Vec<GooseValue<String>>) -> Option<String> {
1149 for value in values {
1150 if let Some(v) = value.value {
1151 if value.filter {
1152 continue;
1153 } else {
1154 if !value.message.is_empty() {
1155 info!("{} = {}", value.message, v)
1156 }
1157 return Some(v);
1158 }
1159 }
1160 }
1161 None
1162 }
1163}
1164impl GooseConfigure<Vec<String>> for GooseConfiguration {
1165 fn get_value(&self, values: Vec<GooseValue<Vec<String>>>) -> Option<Vec<String>> {
1167 for value in values {
1168 if let Some(v) = value.value {
1169 if value.filter {
1170 continue;
1171 } else {
1172 if !value.message.is_empty() {
1173 info!("{} = {:?}", value.message, v)
1174 }
1175 return Some(v);
1176 }
1177 }
1178 }
1179 None
1180 }
1181}
1182impl GooseConfigure<bool> for GooseConfiguration {
1183 fn get_value(&self, values: Vec<GooseValue<bool>>) -> Option<bool> {
1185 for value in values {
1186 if let Some(v) = value.value {
1187 if value.filter {
1188 continue;
1189 } else {
1190 if !value.message.is_empty() {
1191 info!("{} = {}", value.message, v)
1192 }
1193 return Some(v);
1194 }
1195 }
1196 }
1197 None
1198 }
1199}
1200impl GooseConfigure<GooseLogFormat> for GooseConfiguration {
1201 fn get_value(&self, values: Vec<GooseValue<GooseLogFormat>>) -> Option<GooseLogFormat> {
1203 for value in values {
1204 if let Some(v) = value.value {
1205 if value.filter {
1206 continue;
1207 } else {
1208 if !value.message.is_empty() {
1209 info!("{} = {:?}", value.message, v)
1210 }
1211 return Some(v);
1212 }
1213 }
1214 }
1215 None
1216 }
1217}
1218impl GooseConfigure<GooseCoordinatedOmissionMitigation> for GooseConfiguration {
1219 fn get_value(
1221 &self,
1222 values: Vec<GooseValue<GooseCoordinatedOmissionMitigation>>,
1223 ) -> Option<GooseCoordinatedOmissionMitigation> {
1224 for value in values {
1225 if let Some(v) = value.value {
1226 if value.filter {
1227 continue;
1228 } else {
1229 if !value.message.is_empty() {
1230 info!("{} = {:?}", value.message, v)
1231 }
1232 return Some(v);
1233 }
1234 }
1235 }
1236 None
1237 }
1238}
1239impl GooseConfigure<Scenarios> for GooseConfiguration {
1240 fn get_value(&self, values: Vec<GooseValue<Scenarios>>) -> Option<Scenarios> {
1242 for value in values {
1243 if let Some(v) = value.value {
1244 if value.filter {
1245 continue;
1246 } else {
1247 if !value.message.is_empty() {
1248 info!("{} = {:?}", value.message, v)
1249 }
1250 return Some(v);
1251 }
1252 }
1253 }
1254 None
1255 }
1256}
1257
1258impl GooseConfiguration {
1259 pub(crate) fn configure(&mut self, defaults: &GooseDefaults) {
1261 self.quiet = self
1263 .get_value(vec![
1264 GooseValue {
1266 value: Some(self.quiet),
1267 filter: self.quiet == 0,
1268 message: "",
1269 },
1270 GooseValue {
1272 value: defaults.quiet,
1273 filter: defaults.quiet.is_none(),
1274 message: "",
1275 },
1276 ])
1277 .unwrap_or(0);
1278
1279 self.verbose = self
1281 .get_value(vec![
1282 GooseValue {
1284 value: Some(self.verbose),
1285 filter: self.verbose == 0,
1286 message: "",
1287 },
1288 GooseValue {
1290 value: defaults.verbose,
1291 filter: defaults.verbose.is_none(),
1292 message: "",
1293 },
1294 ])
1295 .unwrap_or(0);
1296
1297 self.log_level = self
1299 .get_value(vec![
1300 GooseValue {
1302 value: Some(self.log_level),
1303 filter: self.log_level == 0,
1304 message: "",
1305 },
1306 GooseValue {
1308 value: defaults.log_level,
1309 filter: defaults.log_level.is_none(),
1310 message: "",
1311 },
1312 ])
1313 .unwrap_or(0);
1314
1315 self.goose_log = self
1317 .get_value(vec![
1318 GooseValue {
1320 value: Some(self.goose_log.to_string()),
1321 filter: self.goose_log.is_empty(),
1322 message: "",
1323 },
1324 GooseValue {
1326 value: defaults.goose_log.clone(),
1327 filter: defaults.goose_log.is_none(),
1328 message: "",
1329 },
1330 ])
1331 .unwrap_or_default();
1332
1333 self.initialize_goose_logger();
1335
1336 self.configure_loggers(defaults);
1338
1339 self.test_plan = self.get_value(vec![
1341 GooseValue {
1343 value: self.test_plan.clone(),
1344 filter: self.test_plan.is_none(),
1345 message: "test_plan",
1346 },
1347 GooseValue {
1349 value: defaults.test_plan.clone(),
1350 filter: defaults.test_plan.is_none(),
1351 message: "test_plan",
1352 },
1353 ]);
1354
1355 let default_users = match std::thread::available_parallelism() {
1357 Ok(ap) => Some(ap.get()),
1358 Err(e) => {
1359 info!("failed to detect available_parallelism: {}", e);
1361 Some(1)
1362 }
1363 };
1364
1365 self.users = self.get_value(vec![
1367 GooseValue {
1369 value: self.users,
1370 filter: false,
1371 message: "users",
1372 },
1373 GooseValue {
1375 value: defaults.users,
1376 filter: defaults.users.is_none(),
1377 message: "users",
1378 },
1379 GooseValue {
1381 value: default_users,
1382 filter: self.test_plan.is_some(),
1383 message: "users defaulted to number of CPUs",
1384 },
1385 ]);
1386
1387 self.startup_time = self
1389 .get_value(vec![
1390 GooseValue {
1392 value: Some(util::parse_timespan(&self.startup_time)),
1393 filter: util::parse_timespan(&self.startup_time) == 0,
1394 message: "startup_time",
1395 },
1396 GooseValue {
1398 value: defaults.startup_time,
1399 filter: defaults.startup_time.is_none(),
1400 message: "startup_time",
1401 },
1402 ])
1403 .map_or_else(|| "0".to_string(), |v| v.to_string());
1404
1405 self.run_time = self
1407 .get_value(vec![
1408 GooseValue {
1410 value: Some(util::parse_timespan(&self.run_time)),
1411 filter: util::parse_timespan(&self.run_time) == 0,
1412 message: "run_time",
1413 },
1414 GooseValue {
1416 value: defaults.run_time,
1417 filter: defaults.run_time.is_none(),
1418 message: "run_time",
1419 },
1420 ])
1421 .map_or_else(|| "0".to_string(), |v| v.to_string());
1422
1423 self.hatch_rate = self
1425 .get_value(vec![
1426 GooseValue {
1428 value: Some(util::get_hatch_rate(self.hatch_rate.clone())),
1429 filter: self.hatch_rate.is_none(),
1430 message: "hatch_rate",
1431 },
1432 GooseValue {
1434 value: Some(util::get_hatch_rate(defaults.hatch_rate.clone())),
1435 filter: defaults.hatch_rate.is_none(),
1436 message: "hatch_rate",
1437 },
1438 ])
1439 .map(|v| v.to_string());
1440
1441 self.timeout = self
1443 .get_value(vec![
1444 GooseValue {
1446 value: util::get_float_from_string(self.timeout.clone()),
1447 filter: self.timeout.is_none(),
1448 message: "timeout",
1449 },
1450 GooseValue {
1452 value: util::get_float_from_string(defaults.timeout.clone()),
1453 filter: defaults.timeout.is_none(),
1454 message: "timeout",
1455 },
1456 ])
1457 .map(|v| v.to_string());
1458
1459 self.running_metrics = self.get_value(vec![
1461 GooseValue {
1463 value: self.running_metrics,
1464 filter: self.running_metrics.is_none(),
1465 message: "running_metrics",
1466 },
1467 GooseValue {
1469 value: defaults.running_metrics,
1470 filter: defaults.running_metrics.is_none(),
1471 message: "running_metrics",
1472 },
1473 ]);
1474
1475 self.no_reset_metrics = self
1477 .get_value(vec![
1478 GooseValue {
1480 value: Some(self.no_reset_metrics),
1481 filter: !self.no_reset_metrics,
1482 message: "no_reset_metrics",
1483 },
1484 GooseValue {
1486 value: defaults.no_reset_metrics,
1487 filter: defaults.no_reset_metrics.is_none(),
1488 message: "no_reset_metrics",
1489 },
1490 ])
1491 .unwrap_or(false);
1492
1493 self.no_metrics = self
1495 .get_value(vec![
1496 GooseValue {
1498 value: Some(self.no_metrics),
1499 filter: !self.no_metrics,
1500 message: "no_metrics",
1501 },
1502 GooseValue {
1504 value: defaults.no_metrics,
1505 filter: defaults.no_metrics.is_none(),
1506 message: "no_metrics",
1507 },
1508 ])
1509 .unwrap_or(false);
1510
1511 self.no_transaction_metrics = self
1513 .get_value(vec![
1514 GooseValue {
1516 value: Some(self.no_transaction_metrics),
1517 filter: !self.no_transaction_metrics,
1518 message: "no_transaction_metrics",
1519 },
1520 GooseValue {
1522 value: defaults.no_transaction_metrics,
1523 filter: defaults.no_transaction_metrics.is_none(),
1524 message: "no_transaction_metrics",
1525 },
1526 ])
1527 .unwrap_or(false);
1528
1529 self.no_scenario_metrics = self
1531 .get_value(vec![
1532 GooseValue {
1534 value: Some(self.no_scenario_metrics),
1535 filter: !self.no_scenario_metrics,
1536 message: "no_scenario_metrics",
1537 },
1538 GooseValue {
1540 value: defaults.no_scenario_metrics,
1541 filter: defaults.no_scenario_metrics.is_none(),
1542 message: "no_scenario_metrics",
1543 },
1544 ])
1545 .unwrap_or(false);
1546
1547 self.no_print_metrics = self
1549 .get_value(vec![
1550 GooseValue {
1552 value: Some(self.no_print_metrics),
1553 filter: !self.no_print_metrics,
1554 message: "no_print_metrics",
1555 },
1556 GooseValue {
1558 value: defaults.no_print_metrics,
1559 filter: defaults.no_print_metrics.is_none(),
1560 message: "no_print_metrics",
1561 },
1562 ])
1563 .unwrap_or(false);
1564
1565 self.no_error_summary = self
1567 .get_value(vec![
1568 GooseValue {
1570 value: Some(self.no_error_summary),
1571 filter: !self.no_error_summary,
1572 message: "no_error_summary",
1573 },
1574 GooseValue {
1576 value: defaults.no_error_summary,
1577 filter: defaults.no_error_summary.is_none(),
1578 message: "no_error_summary",
1579 },
1580 ])
1581 .unwrap_or(false);
1582
1583 self.report_file = self
1585 .get_value(vec![
1586 GooseValue {
1588 value: Some(self.report_file.clone()),
1589 filter: self.report_file.is_empty(),
1590 message: "report_file",
1591 },
1592 GooseValue {
1594 value: defaults.report_file.clone(),
1595 filter: defaults.report_file.is_none(),
1596 message: "report_file",
1597 },
1598 ])
1599 .unwrap_or_default();
1600
1601 self.no_debug_body = self
1603 .get_value(vec![
1604 GooseValue {
1606 value: Some(self.no_granular_report),
1607 filter: !self.no_granular_report,
1608 message: "no_granular_report",
1609 },
1610 GooseValue {
1612 value: defaults.no_debug_body,
1613 filter: defaults.no_debug_body.is_none(),
1614 message: "no_granular_report",
1615 },
1616 ])
1617 .unwrap_or(false);
1618
1619 self.iterations = self
1621 .get_value(vec![
1622 GooseValue {
1624 value: Some(self.iterations),
1625 filter: false,
1626 message: "iterations",
1627 },
1628 GooseValue {
1630 value: defaults.iterations,
1631 filter: defaults.iterations.is_none(),
1632 message: "iterations",
1633 },
1634 ])
1635 .unwrap_or(0);
1636
1637 self.scenarios = self
1639 .get_value(vec![
1640 GooseValue {
1642 value: Some(self.scenarios.clone()),
1643 filter: self.scenarios.active.is_empty(),
1644 message: "scenarios",
1645 },
1646 GooseValue {
1648 value: defaults.scenarios.clone(),
1649 filter: defaults.scenarios.is_none(),
1650 message: "scenarios",
1651 },
1652 ])
1653 .unwrap_or_else(|| Scenarios { active: Vec::new() });
1654
1655 self.no_debug_body = self
1657 .get_value(vec![
1658 GooseValue {
1660 value: Some(self.no_debug_body),
1661 filter: !self.no_debug_body,
1662 message: "no_debug_body",
1663 },
1664 GooseValue {
1666 value: defaults.no_debug_body,
1667 filter: defaults.no_debug_body.is_none(),
1668 message: "no_debug_body",
1669 },
1670 ])
1671 .unwrap_or(false);
1672
1673 self.no_status_codes = self
1675 .get_value(vec![
1676 GooseValue {
1678 value: Some(self.no_status_codes),
1679 filter: !self.no_status_codes,
1680 message: "no_status_codes",
1681 },
1682 GooseValue {
1684 value: defaults.no_status_codes,
1685 filter: defaults.no_status_codes.is_none(),
1686 message: "no_status_codes",
1687 },
1688 ])
1689 .unwrap_or(false);
1690
1691 self.no_telnet = self
1693 .get_value(vec![
1694 GooseValue {
1696 value: Some(self.no_telnet),
1697 filter: !self.no_telnet,
1698 message: "no_telnet",
1699 },
1700 GooseValue {
1702 value: defaults.no_telnet,
1703 filter: defaults.no_telnet.is_none(),
1704 message: "",
1705 },
1706 ])
1707 .unwrap_or(false);
1708
1709 self.no_websocket = self
1711 .get_value(vec![
1712 GooseValue {
1714 value: Some(self.no_websocket),
1715 filter: !self.no_websocket,
1716 message: "no_websocket",
1717 },
1718 GooseValue {
1720 value: defaults.no_websocket,
1721 filter: defaults.no_websocket.is_none(),
1722 message: "",
1723 },
1724 ])
1725 .unwrap_or(false);
1726
1727 self.no_autostart = self
1729 .get_value(vec![
1730 GooseValue {
1732 value: Some(self.no_autostart),
1733 filter: !self.no_autostart,
1734 message: "no_autostart",
1735 },
1736 GooseValue {
1738 value: defaults.no_autostart,
1739 filter: defaults.no_autostart.is_none(),
1740 message: "no_autostart",
1741 },
1742 ])
1743 .unwrap_or(false);
1744
1745 self.no_gzip = self
1747 .get_value(vec![
1748 GooseValue {
1750 value: Some(self.no_gzip),
1751 filter: !self.no_gzip,
1752 message: "no_gzip",
1753 },
1754 GooseValue {
1756 value: defaults.no_gzip,
1757 filter: defaults.no_gzip.is_none(),
1758 message: "no_gzip",
1759 },
1760 ])
1761 .unwrap_or(false);
1762
1763 self.accept_invalid_certs = self
1765 .get_value(vec![
1766 GooseValue {
1768 value: Some(self.accept_invalid_certs),
1769 filter: !self.accept_invalid_certs,
1770 message: "accept_invalid_certs",
1771 },
1772 GooseValue {
1774 value: defaults.accept_invalid_certs,
1775 filter: defaults.accept_invalid_certs.is_none(),
1776 message: "accept_invalid_certs",
1777 },
1778 ])
1779 .unwrap_or(false);
1780
1781 self.co_mitigation = self.get_value(vec![
1782 GooseValue {
1784 value: self.co_mitigation.clone(),
1785 filter: self.co_mitigation.is_none(),
1786 message: "co_mitigation",
1787 },
1788 GooseValue {
1790 value: defaults.co_mitigation.clone(),
1791 filter: defaults.co_mitigation.is_none(),
1792 message: "co_mitigation",
1793 },
1794 GooseValue {
1796 value: Some(GooseCoordinatedOmissionMitigation::Disabled),
1797 filter: false,
1798 message: "",
1799 },
1800 ]);
1801
1802 self.throttle_requests = self
1804 .get_value(vec![
1805 GooseValue {
1807 value: Some(self.throttle_requests),
1808 filter: self.throttle_requests == 0,
1809 message: "throttle_requests",
1810 },
1811 GooseValue {
1813 value: defaults.throttle_requests,
1814 filter: defaults.throttle_requests.is_none(),
1815 message: "throttle_requests",
1816 },
1817 ])
1818 .unwrap_or(0);
1819
1820 self.sticky_follow = self
1822 .get_value(vec![
1823 GooseValue {
1825 value: Some(self.sticky_follow),
1826 filter: !self.sticky_follow,
1827 message: "sticky_follow",
1828 },
1829 GooseValue {
1831 value: defaults.sticky_follow,
1832 filter: defaults.sticky_follow.is_none(),
1833 message: "sticky_follow",
1834 },
1835 ])
1836 .unwrap_or(false);
1837 }
1838
1839 pub(crate) fn validate(&self) -> Result<(), GooseError> {
1841 if self.verbose > 0 && self.quiet > 0 {
1843 return Err(GooseError::InvalidOption {
1844 option: "`configuration.verbose`".to_string(),
1845 value: self.verbose.to_string(),
1846 detail: "`configuration.verbose` can not be set with `configuration.quiet`."
1847 .to_string(),
1848 });
1849 }
1850
1851 if let Some(hatch_rate) = self.hatch_rate.as_ref() {
1853 if hatch_rate == "0" {
1854 return Err(GooseError::InvalidOption {
1855 option: "`configuration.hatch_rate`".to_string(),
1856 value: hatch_rate.to_string(),
1857 detail: "`configuration.hatch_rate` must be set to at least 1.".to_string(),
1858 });
1859 }
1860 }
1861
1862 if let Some(timeout) = self.timeout.as_ref() {
1864 if crate::util::get_float_from_string(self.timeout.clone())
1865 .expect("failed to re-convert string to float")
1866 <= 0.0
1867 {
1868 return Err(GooseError::InvalidOption {
1869 option: "`configuration.timeout`".to_string(),
1870 value: timeout.to_string(),
1871 detail: "`configuration.timeout` must be greater than 0.".to_string(),
1872 });
1873 }
1874 }
1875
1876 if let Some(users) = self.users.as_ref() {
1878 if users == &0 {
1879 return Err(GooseError::InvalidOption {
1880 option: "configuration.users".to_string(),
1881 value: users.to_string(),
1882 detail: "`configuration.users` must be set to at least 1.".to_string(),
1883 });
1884 }
1885 }
1886
1887 if self.startup_time != "0" {
1889 if self.hatch_rate.is_some() {
1891 return Err(GooseError::InvalidOption {
1892 option: "`configuration.startup_time`".to_string(),
1893 value: self.startup_time.to_string(),
1894 detail: "`configuration.startup_time` can not be set with `configuration.hatch_rate`.".to_string(),
1895 });
1896 }
1897
1898 if let Some(users) = self.users.as_ref() {
1900 if users < &2 {
1901 return Err(GooseError::InvalidOption {
1902 option: "configuration.users".to_string(),
1903 value: users.to_string(),
1904 detail: "`configuration.users` must be set to at least 2 when `configuration.startup_time` is set.".to_string(),
1905 });
1906 }
1907 }
1908 }
1909
1910 if self.test_plan.is_some() {
1912 if let Some(users) = self.users.as_ref() {
1914 return Err(GooseError::InvalidOption {
1915 option: "`configuration.users`".to_string(),
1916 value: users.to_string(),
1917 detail: "`configuration.users` can not be set with `configuration.test_plan`."
1918 .to_string(),
1919 });
1920 }
1921 if self.startup_time != "0" {
1923 return Err(GooseError::InvalidOption {
1924 option: "`configuration.startup_time`".to_string(),
1925 value: self.startup_time.to_string(),
1926 detail: "`configuration.startup_time` can not be set with `configuration.test_plan`.".to_string(),
1927 });
1928 }
1929 if let Some(hatch_rate) = self.hatch_rate.as_ref() {
1931 return Err(GooseError::InvalidOption {
1932 option: "`configuration.hatch_rate`".to_string(),
1933 value: hatch_rate.to_string(),
1934 detail:
1935 "`configuration.hatch_rate` can not be set with `configuration.test_plan`."
1936 .to_string(),
1937 });
1938 }
1939 if self.run_time != "0" {
1941 return Err(GooseError::InvalidOption {
1942 option: "`configuration.run_time`".to_string(),
1943 value: self.run_time.to_string(),
1944 detail:
1945 "`configuration.run_time` can not be set with `configuration.test_plan`."
1946 .to_string(),
1947 });
1948 }
1949 if self.no_reset_metrics {
1951 return Err(GooseError::InvalidOption {
1952 option: "`configuration.no_reset_metrics".to_string(),
1953 value: self.no_reset_metrics.to_string(),
1954 detail: "`configuration.no_reset_metrics` can not be set with `configuration.test_plan` (metrics are not reset)."
1955 .to_string(),
1956 });
1957 }
1958 }
1959
1960 if self.iterations > 0 {
1962 if self.run_time != "0" {
1964 return Err(GooseError::InvalidOption {
1965 option: "`configuration.run_time`".to_string(),
1966 value: self.run_time.to_string(),
1967 detail:
1968 "`configuration.run_time` can not be set with `configuration.iterations`."
1969 .to_string(),
1970 });
1971 }
1972 if self.test_plan.is_some() {
1974 return Err(GooseError::InvalidOption {
1975 option: "`configuration.iterations`".to_string(),
1976 value: self.iterations.to_string(),
1977 detail:
1978 "`configuration.iteratoins` can not be set with `configuration.test_plan`."
1979 .to_string(),
1980 });
1981 }
1982 if self.no_reset_metrics {
1984 return Err(GooseError::InvalidOption {
1985 option: "`configuration.no_reset_metrics".to_string(),
1986 value: self.no_reset_metrics.to_string(),
1987 detail: "`configuration.no_reset_metrics` can not be set with `configuration.iterations` (metrics are not reset)."
1988 .to_string(),
1989 });
1990 }
1991 }
1992
1993 if self.no_metrics {
1995 if !self.request_log.is_empty() {
1997 return Err(GooseError::InvalidOption {
1998 option: "`configuration.request_log`".to_string(),
1999 value: self.request_log.to_string(),
2000 detail: "`configuration.request_log` can not be set with `configuration.no_metrics`.".to_string(),
2001 });
2002 } else if !self.transaction_log.is_empty() {
2004 return Err(GooseError::InvalidOption {
2005 option: "`configuration.transaction_log`".to_string(),
2006 value: self.transaction_log.to_string(),
2007 detail:
2008 "`configuration.transaction_log` can not be set with `configuration.no_metrics`."
2009 .to_string(),
2010 });
2011 } else if !self.scenario_log.is_empty() {
2013 return Err(GooseError::InvalidOption {
2014 option: "`configuration.scenario_log`".to_string(),
2015 value: self.scenario_log.to_string(),
2016 detail:
2017 "`configuration.scenario_log` can not be set with `configuration.no_metrics`."
2018 .to_string(),
2019 });
2020 } else if !self.error_log.is_empty() {
2022 return Err(GooseError::InvalidOption {
2023 option: "`configuration.error_log`".to_string(),
2024 value: self.error_log.to_string(),
2025 detail:
2026 "`configuration.error_log` can not be set with `configuration.no_metrics`."
2027 .to_string(),
2028 });
2029 } else if !self.report_file.is_empty() {
2031 return Err(GooseError::InvalidOption {
2032 option: "`configuration.report_file`".to_string(),
2033 value: format!("{:?}", self.report_file),
2034 detail:
2035 "`configuration.report_file` can not be set with `configuration.no_metrics`."
2036 .to_string(),
2037 });
2038 } else if self.co_mitigation.as_ref().unwrap()
2040 != &GooseCoordinatedOmissionMitigation::Disabled
2041 {
2042 return Err(GooseError::InvalidOption {
2043 option: "`configuration.co_mitigation`".to_string(),
2044 value: format!("{:?}", self.co_mitigation.as_ref().unwrap()),
2045 detail: "`configuration.co_mitigation` can not be set with `configuration.no_metrircs`."
2046 .to_string(),
2047 });
2048 }
2049 }
2050
2051 if self.report_file.is_empty() && self.no_granular_report {
2052 return Err(GooseError::InvalidOption {
2053 option: "`configuration.no_granular_report`".to_string(),
2054 value: true.to_string(),
2055 detail:
2056 "`configuration.no_granular_report` can not be set without `configuration.report_file`."
2057 .to_string(),
2058 });
2059 }
2060
2061 if self.no_autostart && self.no_telnet && self.no_websocket {
2063 return Err(GooseError::InvalidOption {
2064 option: "`configuration.no_autostart`".to_string(),
2065 value: true.to_string(),
2066 detail: "`configuration.no_autostart` requires at least one Controller be enabled"
2067 .to_string(),
2068 });
2069 }
2070
2071 if self.throttle_requests > 0 {
2091 if self.throttle_requests == 0 {
2093 return Err(GooseError::InvalidOption {
2094 option: "`configuration.throttle_requests`".to_string(),
2095 value: self.throttle_requests.to_string(),
2096 detail: "`configuration.throttle_requests` must be set to at least 1 request per second.".to_string(),
2097 });
2098 } else if self.throttle_requests > 1_000_000 {
2099 return Err(GooseError::InvalidOption {
2100 option: "`configuration.throttle_requests`".to_string(),
2101 value: self.throttle_requests.to_string(),
2102 detail: "`configuration.throttle_requests` can not be set to more than 1,000,000 request per second.".to_string(),
2103 });
2104 }
2105 }
2106
2107 Ok(())
2108 }
2109
2110 pub(crate) fn initialize_goose_logger(&self) {
2113 let debug_level = match self.verbose {
2115 0 => match self.quiet {
2116 0 => LevelFilter::Info,
2117 _ => LevelFilter::Warn,
2118 },
2119 1 => LevelFilter::Debug,
2120 _ => LevelFilter::Trace,
2121 };
2122
2123 let log_level = match self.log_level {
2125 0 => LevelFilter::Warn,
2126 1 => LevelFilter::Info,
2127 2 => LevelFilter::Debug,
2128 _ => LevelFilter::Trace,
2129 };
2130
2131 let goose_log: Option<PathBuf> = if !self.goose_log.is_empty() {
2133 Some(PathBuf::from(&self.goose_log))
2134 } else {
2136 None
2137 };
2138
2139 if let Some(log_to_file) = goose_log {
2140 match CombinedLogger::init(vec![
2141 SimpleLogger::new(debug_level, Config::default()),
2142 WriteLogger::new(
2143 log_level,
2144 Config::default(),
2145 std::fs::File::create(&log_to_file).unwrap(),
2146 ),
2147 ]) {
2148 Ok(_) => (),
2149 Err(e) => {
2150 info!("failed to initialize CombinedLogger: {}", e);
2151 }
2152 }
2153 info!("Writing to log file: {}", log_to_file.display());
2154 } else {
2155 match CombinedLogger::init(vec![SimpleLogger::new(debug_level, Config::default())]) {
2156 Ok(_) => (),
2157 Err(e) => {
2158 info!("failed to initialize CombinedLogger: {}", e);
2159 }
2160 }
2161 }
2162
2163 info!("Output verbosity level: {}", debug_level);
2164 info!("Logfile verbosity level: {}", log_level);
2165 }
2166}
2167
2168#[cfg(test)]
2169mod test {
2170 use super::*;
2171
2172 #[test]
2173 fn set_defaults() {
2174 let host = "http://example.com/".to_string();
2175 let users: usize = 10;
2176 let run_time: usize = 10;
2177 let hatch_rate = "2".to_string();
2178 let timeout = "45".to_string();
2179 let log_level: usize = 1;
2180 let goose_log = "custom-goose.log".to_string();
2181 let quiet: usize = 0;
2182 let verbose: usize = 0;
2183 let report_file = "custom-goose-report.html".to_string();
2184 let request_log = "custom-goose-request.log".to_string();
2185 let transaction_log = "custom-goose-transaction.log".to_string();
2186 let scenario_log = "custom-goose-scenario.log".to_string();
2187 let debug_log = "custom-goose-debug.log".to_string();
2188 let error_log = "custom-goose-error.log".to_string();
2189 let throttle_requests: usize = 25;
2190
2191 let goose_attack = GooseAttack::initialize()
2192 .unwrap()
2193 .set_default(GooseDefault::Host, host.as_str())
2194 .unwrap()
2195 .set_default(GooseDefault::Users, users)
2196 .unwrap()
2197 .set_default(GooseDefault::RunTime, run_time)
2198 .unwrap()
2199 .set_default(GooseDefault::HatchRate, hatch_rate.as_str())
2200 .unwrap()
2201 .set_default(GooseDefault::LogLevel, log_level)
2202 .unwrap()
2203 .set_default(GooseDefault::GooseLog, goose_log.as_str())
2204 .unwrap()
2205 .set_default(GooseDefault::Quiet, quiet)
2206 .unwrap()
2207 .set_default(GooseDefault::Verbose, verbose)
2208 .unwrap()
2209 .set_default(GooseDefault::Timeout, timeout.as_str())
2210 .unwrap()
2211 .set_default(GooseDefault::RunningMetrics, 15)
2212 .unwrap()
2213 .set_default(GooseDefault::NoResetMetrics, true)
2214 .unwrap()
2215 .set_default(GooseDefault::NoMetrics, true)
2216 .unwrap()
2217 .set_default(GooseDefault::NoTransactionMetrics, true)
2218 .unwrap()
2219 .set_default(GooseDefault::NoScenarioMetrics, true)
2220 .unwrap()
2221 .set_default(GooseDefault::NoPrintMetrics, true)
2222 .unwrap()
2223 .set_default(GooseDefault::NoErrorSummary, true)
2224 .unwrap()
2225 .set_default(GooseDefault::NoTelnet, true)
2226 .unwrap()
2227 .set_default(GooseDefault::NoWebSocket, true)
2228 .unwrap()
2229 .set_default(GooseDefault::NoAutoStart, true)
2230 .unwrap()
2231 .set_default(GooseDefault::NoGzip, true)
2232 .unwrap()
2233 .set_default(GooseDefault::ReportFile, report_file.as_str())
2234 .unwrap()
2235 .set_default(GooseDefault::RequestLog, request_log.as_str())
2236 .unwrap()
2237 .set_default(GooseDefault::RequestFormat, GooseLogFormat::Raw)
2238 .unwrap()
2239 .set_default(GooseDefault::RequestBody, true)
2240 .unwrap()
2241 .set_default(GooseDefault::TransactionLog, transaction_log.as_str())
2242 .unwrap()
2243 .set_default(GooseDefault::TransactionFormat, GooseLogFormat::Raw)
2244 .unwrap()
2245 .set_default(GooseDefault::ScenarioLog, scenario_log.as_str())
2246 .unwrap()
2247 .set_default(GooseDefault::ScenarioFormat, GooseLogFormat::Raw)
2248 .unwrap()
2249 .set_default(GooseDefault::ErrorLog, error_log.as_str())
2250 .unwrap()
2251 .set_default(GooseDefault::ErrorFormat, GooseLogFormat::Csv)
2252 .unwrap()
2253 .set_default(GooseDefault::DebugLog, debug_log.as_str())
2254 .unwrap()
2255 .set_default(GooseDefault::DebugFormat, GooseLogFormat::Csv)
2256 .unwrap()
2257 .set_default(GooseDefault::NoDebugBody, true)
2258 .unwrap()
2259 .set_default(GooseDefault::NoStatusCodes, true)
2260 .unwrap()
2261 .set_default(
2262 GooseDefault::CoordinatedOmissionMitigation,
2263 GooseCoordinatedOmissionMitigation::Disabled,
2264 )
2265 .unwrap()
2266 .set_default(GooseDefault::ThrottleRequests, throttle_requests)
2267 .unwrap()
2268 .set_default(GooseDefault::StickyFollow, true)
2269 .unwrap();
2270
2271 assert!(goose_attack.defaults.host == Some(host));
2272 assert!(goose_attack.defaults.users == Some(users));
2273 assert!(goose_attack.defaults.run_time == Some(run_time));
2274 assert!(goose_attack.defaults.hatch_rate == Some(hatch_rate));
2275 assert!(goose_attack.defaults.log_level == Some(log_level as u8));
2276 assert!(goose_attack.defaults.goose_log == Some(goose_log));
2277 assert!(goose_attack.defaults.request_body == Some(true));
2278 assert!(goose_attack.defaults.no_debug_body == Some(true));
2279 assert!(goose_attack.defaults.quiet == Some(quiet as u8));
2280 assert!(goose_attack.defaults.verbose == Some(verbose as u8));
2281 assert!(goose_attack.defaults.running_metrics == Some(15));
2282 assert!(goose_attack.defaults.no_reset_metrics == Some(true));
2283 assert!(goose_attack.defaults.no_metrics == Some(true));
2284 assert!(goose_attack.defaults.no_transaction_metrics == Some(true));
2285 assert!(goose_attack.defaults.no_scenario_metrics == Some(true));
2286 assert!(goose_attack.defaults.no_print_metrics == Some(true));
2287 assert!(goose_attack.defaults.no_error_summary == Some(true));
2288 assert!(goose_attack.defaults.no_telnet == Some(true));
2289 assert!(goose_attack.defaults.no_websocket == Some(true));
2290 assert!(goose_attack.defaults.no_autostart == Some(true));
2291 assert!(goose_attack.defaults.timeout == Some(timeout));
2292 assert!(goose_attack.defaults.no_gzip == Some(true));
2293 assert!(goose_attack.defaults.report_file == Some(vec![report_file]));
2294 assert!(goose_attack.defaults.request_log == Some(request_log));
2295 assert!(goose_attack.defaults.request_format == Some(GooseLogFormat::Raw));
2296 assert!(goose_attack.defaults.error_log == Some(error_log));
2297 assert!(goose_attack.defaults.error_format == Some(GooseLogFormat::Csv));
2298 assert!(goose_attack.defaults.debug_log == Some(debug_log));
2299 assert!(goose_attack.defaults.debug_format == Some(GooseLogFormat::Csv));
2300 assert!(goose_attack.defaults.no_status_codes == Some(true));
2301 assert!(
2302 goose_attack.defaults.co_mitigation
2303 == Some(GooseCoordinatedOmissionMitigation::Disabled)
2304 );
2305 assert!(goose_attack.defaults.throttle_requests == Some(throttle_requests));
2306 assert!(goose_attack.defaults.sticky_follow == Some(true));
2307 }
2308}