use gumdrop::Options;
use serde::{Deserialize, Serialize};
use simplelog::*;
use std::path::PathBuf;
use crate::logger::GooseLogFormat;
use crate::metrics::GooseCoordinatedOmissionMitigation;
use crate::util;
use crate::{GooseAttack, GooseError};
const DEFAULT_PORT: &str = "5115";
#[derive(Options, Debug, Clone, Serialize, Deserialize)]
pub struct GooseConfiguration {
#[options(short = "h")]
pub help: bool,
#[options(short = "V")]
pub version: bool,
#[options(short = "l", help = "Lists all tasks and exits\n")]
pub list: bool,
#[options(short = "H")]
pub host: String,
#[options(short = "u")]
pub users: Option<usize>,
#[options(short = "r", meta = "RATE")]
pub hatch_rate: Option<String>,
#[options(short = "s", meta = "TIME")]
pub startup_time: String,
#[options(short = "t", meta = "TIME")]
pub run_time: String,
#[options(short = "G", meta = "NAME")]
pub goose_log: String,
#[options(short = "g", count)]
pub log_level: u8,
#[options(
count,
short = "v",
// Add a blank line and then a 'Metrics:' header after this option
help = "Sets Goose verbosity (-v, -vv, etc)\n\nMetrics:"
)]
pub verbose: u8,
#[options(no_short, meta = "TIME")]
pub running_metrics: Option<usize>,
#[options(no_short)]
pub no_reset_metrics: bool,
#[options(no_short)]
pub no_metrics: bool,
#[options(no_short)]
pub no_task_metrics: bool,
#[options(no_short)]
pub no_error_summary: bool,
#[options(no_short, meta = "NAME")]
pub report_file: String,
#[options(short = "R", meta = "NAME")]
pub request_log: String,
#[options(no_short, meta = "FORMAT")]
pub request_format: Option<GooseLogFormat>,
#[options(no_short)]
pub request_body: bool,
#[options(short = "T", meta = "NAME")]
pub task_log: String,
#[options(no_short, meta = "FORMAT")]
pub task_format: Option<GooseLogFormat>,
#[options(short = "E", meta = "NAME")]
pub error_log: String,
#[options(no_short, meta = "FORMAT")]
pub error_format: Option<GooseLogFormat>,
#[options(short = "D", meta = "NAME")]
pub debug_log: String,
#[options(no_short, meta = "FORMAT")]
pub debug_format: Option<GooseLogFormat>,
#[options(no_short)]
pub no_debug_body: bool,
#[options(no_short, help = "Tracks additional status code metrics\n\nAdvanced:")]
pub status_codes: bool,
#[options(no_short)]
pub no_telnet: bool,
#[options(no_short, meta = "HOST")]
pub telnet_host: String,
#[options(no_short, meta = "PORT")]
pub telnet_port: u16,
#[options(no_short)]
pub no_websocket: bool,
#[options(no_short, meta = "HOST")]
pub websocket_host: String,
#[options(no_short, meta = "PORT")]
pub websocket_port: u16,
#[options(no_short)]
pub no_autostart: bool,
#[options(no_short)]
pub no_gzip: bool,
#[options(no_short, meta = "VALUE")]
pub timeout: Option<String>,
#[options(no_short, meta = "STRATEGY")]
pub co_mitigation: Option<GooseCoordinatedOmissionMitigation>,
#[options(no_short, meta = "VALUE")]
pub throttle_requests: usize,
#[options(
no_short,
help = "Follows base_url redirect with subsequent requests\n\nGaggle:"
)]
pub sticky_follow: bool,
#[options(no_short)]
pub manager: bool,
#[options(no_short, meta = "VALUE")]
pub expect_workers: Option<usize>,
#[options(no_short)]
pub no_hash_check: bool,
#[options(no_short, meta = "HOST")]
pub manager_bind_host: String,
#[options(no_short, meta = "PORT")]
pub manager_bind_port: u16,
#[options(no_short)]
pub worker: bool,
#[options(no_short, meta = "HOST")]
pub manager_host: String,
#[options(no_short, meta = "PORT")]
pub manager_port: u16,
}
#[derive(Clone, Debug, Default)]
pub(crate) struct GooseDefaults {
pub host: Option<String>,
pub users: Option<usize>,
pub hatch_rate: Option<String>,
pub startup_time: Option<usize>,
pub run_time: Option<usize>,
pub log_level: Option<u8>,
pub goose_log: Option<String>,
pub verbose: Option<u8>,
pub running_metrics: Option<usize>,
pub no_reset_metrics: Option<bool>,
pub no_metrics: Option<bool>,
pub no_task_metrics: Option<bool>,
pub no_error_summary: Option<bool>,
pub report_file: Option<String>,
pub request_log: Option<String>,
pub request_format: Option<GooseLogFormat>,
pub request_body: Option<bool>,
pub task_log: Option<String>,
pub task_format: Option<GooseLogFormat>,
pub error_log: Option<String>,
pub error_format: Option<GooseLogFormat>,
pub debug_log: Option<String>,
pub debug_format: Option<GooseLogFormat>,
pub no_debug_body: Option<bool>,
pub no_telnet: Option<bool>,
pub no_websocket: Option<bool>,
pub no_autostart: Option<bool>,
pub no_gzip: Option<bool>,
pub timeout: Option<String>,
pub co_mitigation: Option<GooseCoordinatedOmissionMitigation>,
pub status_codes: Option<bool>,
pub throttle_requests: Option<usize>,
pub sticky_follow: Option<bool>,
pub manager: Option<bool>,
pub expect_workers: Option<usize>,
pub no_hash_check: Option<bool>,
pub telnet_host: Option<String>,
pub telnet_port: Option<u16>,
pub websocket_host: Option<String>,
pub websocket_port: Option<u16>,
pub manager_bind_host: Option<String>,
pub manager_bind_port: Option<u16>,
pub worker: Option<bool>,
pub manager_host: Option<String>,
pub manager_port: Option<u16>,
}
#[derive(Debug)]
pub enum GooseDefault {
Host,
Users,
HatchRate,
StartupTime,
RunTime,
LogLevel,
GooseLog,
Verbose,
RunningMetrics,
NoResetMetrics,
NoMetrics,
NoTaskMetrics,
NoErrorSummary,
ReportFile,
RequestLog,
RequestFormat,
RequestBody,
TaskLog,
TaskFormat,
ErrorLog,
ErrorFormat,
DebugLog,
DebugFormat,
NoDebugBody,
NoTelnet,
NoWebSocket,
CoordinatedOmissionMitigation,
NoAutoStart,
Timeout,
NoGzip,
StatusCodes,
ThrottleRequests,
StickyFollow,
Manager,
ExpectWorkers,
NoHashCheck,
TelnetHost,
TelnetPort,
WebSocketHost,
WebSocketPort,
ManagerBindHost,
ManagerBindPort,
Worker,
ManagerHost,
ManagerPort,
}
pub trait GooseDefaultType<T> {
fn set_default(self, key: GooseDefault, value: T) -> Result<Box<Self>, GooseError>;
}
impl GooseDefaultType<&str> for GooseAttack {
fn set_default(mut self, key: GooseDefault, value: &str) -> Result<Box<Self>, GooseError> {
match key {
GooseDefault::HatchRate => self.defaults.hatch_rate = Some(value.to_string()),
GooseDefault::Timeout => self.defaults.timeout = Some(value.to_string()),
GooseDefault::Host => self.defaults.host = Some(value.to_string()),
GooseDefault::GooseLog => self.defaults.goose_log = Some(value.to_string()),
GooseDefault::ReportFile => self.defaults.report_file = Some(value.to_string()),
GooseDefault::RequestLog => self.defaults.request_log = Some(value.to_string()),
GooseDefault::TaskLog => self.defaults.task_log = Some(value.to_string()),
GooseDefault::ErrorLog => self.defaults.error_log = Some(value.to_string()),
GooseDefault::DebugLog => self.defaults.debug_log = Some(value.to_string()),
GooseDefault::TelnetHost => self.defaults.telnet_host = Some(value.to_string()),
GooseDefault::WebSocketHost => self.defaults.websocket_host = Some(value.to_string()),
GooseDefault::ManagerBindHost => {
self.defaults.manager_bind_host = Some(value.to_string())
}
GooseDefault::ManagerHost => self.defaults.manager_host = Some(value.to_string()),
GooseDefault::Users
| GooseDefault::StartupTime
| GooseDefault::RunTime
| GooseDefault::LogLevel
| GooseDefault::Verbose
| GooseDefault::ThrottleRequests
| GooseDefault::ExpectWorkers
| GooseDefault::TelnetPort
| GooseDefault::WebSocketPort
| GooseDefault::ManagerBindPort
| GooseDefault::ManagerPort => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: value.to_string(),
detail: format!(
"set_default(GooseDefault::{:?}, {}) expected usize value, received &str",
key, value
),
});
}
GooseDefault::RunningMetrics
| GooseDefault::NoResetMetrics
| GooseDefault::NoMetrics
| GooseDefault::NoTaskMetrics
| GooseDefault::RequestBody
| GooseDefault::NoErrorSummary
| GooseDefault::NoDebugBody
| GooseDefault::NoTelnet
| GooseDefault::NoWebSocket
| GooseDefault::NoAutoStart
| GooseDefault::NoGzip
| GooseDefault::StatusCodes
| GooseDefault::StickyFollow
| GooseDefault::Manager
| GooseDefault::NoHashCheck
| GooseDefault::Worker => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: value.to_string(),
detail: format!(
"set_default(GooseDefault::{:?}, {}) expected bool value, received &str",
key, value
),
});
}
GooseDefault::DebugFormat
| GooseDefault::ErrorFormat
| GooseDefault::TaskFormat
| GooseDefault::RequestFormat => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: value.to_string(),
detail: format!(
"set_default(GooseDefault::{:?}, {}) expected GooseLogFormat value, received &str",
key, value
),
});
}
GooseDefault::CoordinatedOmissionMitigation => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: value.to_string(),
detail: format!(
"set_default(GooseDefault::{:?}, {}) expected GooseCoordinatedOmissionMitigation value, received &str",
key, value
),
});
}
}
Ok(Box::new(self))
}
}
impl GooseDefaultType<usize> for GooseAttack {
fn set_default(mut self, key: GooseDefault, value: usize) -> Result<Box<Self>, GooseError> {
match key {
GooseDefault::Users => self.defaults.users = Some(value),
GooseDefault::StartupTime => self.defaults.startup_time = Some(value),
GooseDefault::RunTime => self.defaults.run_time = Some(value),
GooseDefault::RunningMetrics => self.defaults.running_metrics = Some(value),
GooseDefault::LogLevel => self.defaults.log_level = Some(value as u8),
GooseDefault::Verbose => self.defaults.verbose = Some(value as u8),
GooseDefault::ThrottleRequests => self.defaults.throttle_requests = Some(value),
GooseDefault::ExpectWorkers => self.defaults.expect_workers = Some(value),
GooseDefault::TelnetPort => self.defaults.telnet_port = Some(value as u16),
GooseDefault::WebSocketPort => self.defaults.websocket_port = Some(value as u16),
GooseDefault::ManagerBindPort => self.defaults.manager_bind_port = Some(value as u16),
GooseDefault::ManagerPort => self.defaults.manager_port = Some(value as u16),
GooseDefault::Host
| GooseDefault::HatchRate
| GooseDefault::Timeout
| GooseDefault::GooseLog
| GooseDefault::ReportFile
| GooseDefault::RequestLog
| GooseDefault::TaskLog
| GooseDefault::ErrorLog
| GooseDefault::DebugLog
| GooseDefault::TelnetHost
| GooseDefault::WebSocketHost
| GooseDefault::ManagerBindHost
| GooseDefault::ManagerHost => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: format!("{}", value),
detail: format!(
"set_default(GooseDefault::{:?}, {}) expected &str value, received usize",
key, value
),
})
}
GooseDefault::NoResetMetrics
| GooseDefault::NoMetrics
| GooseDefault::NoTaskMetrics
| GooseDefault::RequestBody
| GooseDefault::NoErrorSummary
| GooseDefault::NoDebugBody
| GooseDefault::NoTelnet
| GooseDefault::NoWebSocket
| GooseDefault::NoAutoStart
| GooseDefault::NoGzip
| GooseDefault::StatusCodes
| GooseDefault::StickyFollow
| GooseDefault::Manager
| GooseDefault::NoHashCheck
| GooseDefault::Worker => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: format!("{}", value),
detail: format!(
"set_default(GooseDefault::{:?}, {}) expected bool value, received usize",
key, value
),
})
}
GooseDefault::RequestFormat
| GooseDefault::DebugFormat
| GooseDefault::ErrorFormat
| GooseDefault::TaskFormat => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: value.to_string(),
detail: format!(
"set_default(GooseDefault::{:?}, {}) expected GooseLogFormat value, received usize",
key, value
),
});
}
GooseDefault::CoordinatedOmissionMitigation => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: value.to_string(),
detail: format!(
"set_default(GooseDefault::{:?}, {}) expected GooseCoordinatedOmissionMitigation value, received usize",
key, value
),
});
}
}
Ok(Box::new(self))
}
}
impl GooseDefaultType<bool> for GooseAttack {
fn set_default(mut self, key: GooseDefault, value: bool) -> Result<Box<Self>, GooseError> {
match key {
GooseDefault::NoResetMetrics => self.defaults.no_reset_metrics = Some(value),
GooseDefault::NoMetrics => self.defaults.no_metrics = Some(value),
GooseDefault::NoTaskMetrics => self.defaults.no_task_metrics = Some(value),
GooseDefault::RequestBody => self.defaults.request_body = Some(value),
GooseDefault::NoErrorSummary => self.defaults.no_error_summary = Some(value),
GooseDefault::NoDebugBody => self.defaults.no_debug_body = Some(value),
GooseDefault::NoTelnet => self.defaults.no_telnet = Some(value),
GooseDefault::NoWebSocket => self.defaults.no_websocket = Some(value),
GooseDefault::NoAutoStart => self.defaults.no_autostart = Some(value),
GooseDefault::NoGzip => self.defaults.no_gzip = Some(value),
GooseDefault::StatusCodes => self.defaults.status_codes = Some(value),
GooseDefault::StickyFollow => self.defaults.sticky_follow = Some(value),
GooseDefault::Manager => self.defaults.manager = Some(value),
GooseDefault::NoHashCheck => self.defaults.no_hash_check = Some(value),
GooseDefault::Worker => self.defaults.worker = Some(value),
GooseDefault::Host
| GooseDefault::GooseLog
| GooseDefault::ReportFile
| GooseDefault::RequestLog
| GooseDefault::TaskLog
| GooseDefault::RunningMetrics
| GooseDefault::ErrorLog
| GooseDefault::DebugLog
| GooseDefault::TelnetHost
| GooseDefault::WebSocketHost
| GooseDefault::ManagerBindHost
| GooseDefault::ManagerHost => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: format!("{}", value),
detail: format!(
"set_default(GooseDefault::{:?}, {}) expected &str value, received bool",
key, value
),
})
}
GooseDefault::Users
| GooseDefault::HatchRate
| GooseDefault::Timeout
| GooseDefault::StartupTime
| GooseDefault::RunTime
| GooseDefault::LogLevel
| GooseDefault::Verbose
| GooseDefault::ThrottleRequests
| GooseDefault::ExpectWorkers
| GooseDefault::TelnetPort
| GooseDefault::WebSocketPort
| GooseDefault::ManagerBindPort
| GooseDefault::ManagerPort => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: format!("{}", value),
detail: format!(
"set_default(GooseDefault::{:?}, {}) expected usize value, received bool",
key, value
),
})
}
GooseDefault::RequestFormat
| GooseDefault::DebugFormat
| GooseDefault::ErrorFormat
| GooseDefault::TaskFormat => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: value.to_string(),
detail: format!(
"set_default(GooseDefault::{:?}, {}) expected GooseLogFormat value, received bool",
key, value
),
});
}
GooseDefault::CoordinatedOmissionMitigation => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: value.to_string(),
detail: format!(
"set_default(GooseDefault::{:?}, {}) expected GooseCoordinatedOmissionMitigation value, received bool",
key, value
),
});
}
}
Ok(Box::new(self))
}
}
impl GooseDefaultType<GooseCoordinatedOmissionMitigation> for GooseAttack {
fn set_default(
mut self,
key: GooseDefault,
value: GooseCoordinatedOmissionMitigation,
) -> Result<Box<Self>, GooseError> {
match key {
GooseDefault::CoordinatedOmissionMitigation => self.defaults.co_mitigation = Some(value),
GooseDefault::NoResetMetrics
| GooseDefault::NoMetrics
| GooseDefault::NoTaskMetrics
| GooseDefault::RequestBody
| GooseDefault::NoErrorSummary
| GooseDefault::NoDebugBody
| GooseDefault::NoTelnet
| GooseDefault::NoWebSocket
| GooseDefault::NoAutoStart
| GooseDefault::NoGzip
| GooseDefault::StatusCodes
| GooseDefault::StickyFollow
| GooseDefault::Manager
| GooseDefault::NoHashCheck
| GooseDefault::Worker => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: format!("{:?}", value),
detail: format!(
"set_default(GooseDefault::{:?}, {:?}) expected bool value, received GooseCoordinatedOmissionMitigation",
key, value
),
})
}
GooseDefault::Host
| GooseDefault::GooseLog
| GooseDefault::ReportFile
| GooseDefault::RequestLog
| GooseDefault::TaskLog
| GooseDefault::RunningMetrics
| GooseDefault::ErrorLog
| GooseDefault::DebugLog
| GooseDefault::TelnetHost
| GooseDefault::WebSocketHost
| GooseDefault::ManagerBindHost
| GooseDefault::ManagerHost => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: format!("{:?}", value),
detail: format!(
"set_default(GooseDefault::{:?}, {:?}) expected &str value, received GooseCoordinatedOmissionMitigation",
key, value
),
})
}
GooseDefault::Users
| GooseDefault::HatchRate
| GooseDefault::Timeout
| GooseDefault::StartupTime
| GooseDefault::RunTime
| GooseDefault::LogLevel
| GooseDefault::Verbose
| GooseDefault::ThrottleRequests
| GooseDefault::ExpectWorkers
| GooseDefault::TelnetPort
| GooseDefault::WebSocketPort
| GooseDefault::ManagerBindPort
| GooseDefault::ManagerPort => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: format!("{:?}", value),
detail: format!(
"set_default(GooseDefault::{:?}, {:?}) expected usize value, received GooseCoordinatedOmissionMitigation",
key, value
),
})
}
GooseDefault::RequestFormat
| GooseDefault::DebugFormat
| GooseDefault::ErrorFormat
| GooseDefault::TaskFormat => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: format!("{:?}", value),
detail: format!(
"set_default(GooseDefault::{:?}, {:?}) expected GooseLogFormat value, received GooseCoordinatedOmissionMitigation",
key, value
),
})
}
}
Ok(Box::new(self))
}
}
impl GooseDefaultType<GooseLogFormat> for GooseAttack {
fn set_default(
mut self,
key: GooseDefault,
value: GooseLogFormat,
) -> Result<Box<Self>, GooseError> {
match key {
GooseDefault::RequestFormat => self.defaults.request_format = Some(value),
GooseDefault::DebugFormat => self.defaults.debug_format = Some(value),
GooseDefault::ErrorFormat => self.defaults.error_format = Some(value),
GooseDefault::TaskFormat => self.defaults.task_format = Some(value),
GooseDefault::NoResetMetrics
| GooseDefault::NoMetrics
| GooseDefault::NoTaskMetrics
| GooseDefault::RequestBody
| GooseDefault::NoErrorSummary
| GooseDefault::NoDebugBody
| GooseDefault::NoTelnet
| GooseDefault::NoWebSocket
| GooseDefault::NoAutoStart
| GooseDefault::NoGzip
| GooseDefault::StatusCodes
| GooseDefault::StickyFollow
| GooseDefault::Manager
| GooseDefault::NoHashCheck
| GooseDefault::Worker => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: format!("{:?}", value),
detail: format!(
"set_default(GooseDefault::{:?}, {:?}) expected bool value, received GooseCoordinatedOmissionMitigation",
key, value
),
})
}
GooseDefault::Host
| GooseDefault::GooseLog
| GooseDefault::ReportFile
| GooseDefault::RequestLog
| GooseDefault::TaskLog
| GooseDefault::RunningMetrics
| GooseDefault::ErrorLog
| GooseDefault::DebugLog
| GooseDefault::TelnetHost
| GooseDefault::WebSocketHost
| GooseDefault::ManagerBindHost
| GooseDefault::ManagerHost => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: format!("{:?}", value),
detail: format!(
"set_default(GooseDefault::{:?}, {:?}) expected &str value, received GooseCoordinatedOmissionMitigation",
key, value
),
})
}
GooseDefault::Users
| GooseDefault::HatchRate
| GooseDefault::Timeout
| GooseDefault::StartupTime
| GooseDefault::RunTime
| GooseDefault::LogLevel
| GooseDefault::Verbose
| GooseDefault::ThrottleRequests
| GooseDefault::ExpectWorkers
| GooseDefault::TelnetPort
| GooseDefault::WebSocketPort
| GooseDefault::ManagerBindPort
| GooseDefault::ManagerPort => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: format!("{:?}", value),
detail: format!(
"set_default(GooseDefault::{:?}, {:?}) expected usize value, received GooseCoordinatedOmissionMitigation",
key, value
),
})
}
GooseDefault::CoordinatedOmissionMitigation => {
return Err(GooseError::InvalidOption {
option: format!("GooseDefault::{:?}", key),
value: format!("{:?}", value),
detail: format!(
"set_default(GooseDefault::{:?}, {:?}) expected GooseCoordinatedOmissionMitigation value, received GooseLogFormat",
key, value
),
})
}
}
Ok(Box::new(self))
}
}
#[derive(Debug, Clone)]
pub(crate) struct GooseValue<'a, T> {
pub(crate) value: Option<T>,
pub(crate) filter: bool,
pub(crate) message: &'a str,
}
pub(crate) trait GooseConfigure<T> {
fn get_value(&self, values: Vec<GooseValue<T>>) -> Option<T>;
}
impl GooseConfigure<usize> for GooseConfiguration {
fn get_value(&self, values: Vec<GooseValue<usize>>) -> Option<usize> {
for value in values {
if let Some(v) = value.value {
if value.filter {
continue;
} else {
if !value.message.is_empty() {
info!("{} = {}", value.message, v)
}
return Some(v);
}
}
}
None
}
}
impl GooseConfigure<u16> for GooseConfiguration {
fn get_value(&self, values: Vec<GooseValue<u16>>) -> Option<u16> {
for value in values {
if let Some(v) = value.value {
if value.filter {
continue;
} else {
if !value.message.is_empty() {
info!("{} = {}", value.message, v)
}
return Some(v);
}
}
}
None
}
}
impl GooseConfigure<u8> for GooseConfiguration {
fn get_value(&self, values: Vec<GooseValue<u8>>) -> Option<u8> {
for value in values {
if let Some(v) = value.value {
if value.filter {
continue;
} else {
if !value.message.is_empty() {
info!("{} = {}", value.message, v)
}
return Some(v);
}
}
}
None
}
}
impl GooseConfigure<f32> for GooseConfiguration {
fn get_value(&self, values: Vec<GooseValue<f32>>) -> Option<f32> {
for value in values {
if let Some(v) = value.value {
if value.filter {
continue;
} else {
if !value.message.is_empty() {
info!("{} = {}", value.message, v)
}
return Some(v);
}
}
}
None
}
}
impl GooseConfigure<String> for GooseConfiguration {
fn get_value(&self, values: Vec<GooseValue<String>>) -> Option<String> {
for value in values {
if let Some(v) = value.value {
if value.filter {
continue;
} else {
if !value.message.is_empty() {
info!("{} = {}", value.message, v)
}
return Some(v);
}
}
}
None
}
}
impl GooseConfigure<bool> for GooseConfiguration {
fn get_value(&self, values: Vec<GooseValue<bool>>) -> Option<bool> {
for value in values {
if let Some(v) = value.value {
if value.filter {
continue;
} else {
if !value.message.is_empty() {
info!("{} = {}", value.message, v)
}
return Some(v);
}
}
}
None
}
}
impl GooseConfigure<GooseLogFormat> for GooseConfiguration {
fn get_value(&self, values: Vec<GooseValue<GooseLogFormat>>) -> Option<GooseLogFormat> {
for value in values {
if let Some(v) = value.value {
if value.filter {
continue;
} else {
if !value.message.is_empty() {
info!("{} = {:?}", value.message, v)
}
return Some(v);
}
}
}
None
}
}
impl GooseConfigure<GooseCoordinatedOmissionMitigation> for GooseConfiguration {
fn get_value(
&self,
values: Vec<GooseValue<GooseCoordinatedOmissionMitigation>>,
) -> Option<GooseCoordinatedOmissionMitigation> {
for value in values {
if let Some(v) = value.value {
if value.filter {
continue;
} else {
if !value.message.is_empty() {
info!("{} = {:?}", value.message, v)
}
return Some(v);
}
}
}
None
}
}
impl GooseConfiguration {
pub(crate) fn configure(&mut self, defaults: &GooseDefaults) {
self.verbose = self
.get_value(vec![
GooseValue {
value: Some(self.verbose),
filter: self.verbose == 0,
message: "",
},
GooseValue {
value: defaults.verbose,
filter: defaults.verbose.is_none(),
message: "",
},
])
.unwrap_or(0);
self.log_level = self
.get_value(vec![
GooseValue {
value: Some(self.log_level),
filter: self.log_level == 0,
message: "",
},
GooseValue {
value: defaults.log_level,
filter: defaults.log_level.is_none(),
message: "",
},
])
.unwrap_or(0);
self.goose_log = self
.get_value(vec![
GooseValue {
value: Some(self.goose_log.to_string()),
filter: self.goose_log.is_empty(),
message: "",
},
GooseValue {
value: defaults.goose_log.clone(),
filter: defaults.goose_log.is_none(),
message: "",
},
])
.unwrap_or_else(|| "".to_string());
self.initialize_goose_logger();
self.configure_loggers(defaults);
self.manager = self
.get_value(vec![
GooseValue {
value: Some(self.manager),
filter: !self.manager,
message: "",
},
GooseValue {
value: defaults.manager,
filter: defaults.manager.is_none(),
message: "",
},
])
.unwrap_or(false);
self.worker = self
.get_value(vec![
GooseValue {
value: Some(self.worker),
filter: !self.worker,
message: "",
},
GooseValue {
value: defaults.worker,
filter: defaults.worker.is_none(),
message: "",
},
])
.unwrap_or(false);
self.users = self.get_value(vec![
GooseValue {
value: self.users,
filter: false,
message: "users",
},
GooseValue {
value: defaults.users,
filter: defaults.users.is_none() || self.worker,
message: "users",
},
GooseValue {
value: Some(num_cpus::get()),
filter: self.worker,
message: "users defaulted to number of CPUs",
},
]);
self.startup_time = self
.get_value(vec![
GooseValue {
value: Some(util::parse_timespan(&self.startup_time)),
filter: util::parse_timespan(&self.startup_time) == 0,
message: "startup_time",
},
GooseValue {
value: defaults.startup_time,
filter: defaults.startup_time.is_none() || self.worker,
message: "startup_time",
},
])
.map_or_else(|| "0".to_string(), |v| v.to_string());
self.run_time = self
.get_value(vec![
GooseValue {
value: Some(util::parse_timespan(&self.run_time)),
filter: util::parse_timespan(&self.run_time) == 0,
message: "run_time",
},
GooseValue {
value: defaults.run_time,
filter: defaults.run_time.is_none() || self.worker,
message: "run_time",
},
])
.map_or_else(|| "0".to_string(), |v| v.to_string());
self.hatch_rate = self
.get_value(vec![
GooseValue {
value: Some(util::get_hatch_rate(self.hatch_rate.clone())),
filter: self.hatch_rate.is_none(),
message: "hatch_rate",
},
GooseValue {
value: Some(util::get_hatch_rate(defaults.hatch_rate.clone())),
filter: defaults.hatch_rate.is_none() || self.worker,
message: "hatch_rate",
},
])
.map(|v| v.to_string());
self.timeout = self
.get_value(vec![
GooseValue {
value: util::get_float_from_string(self.timeout.clone()),
filter: self.timeout.is_none(),
message: "timeout",
},
GooseValue {
value: util::get_float_from_string(defaults.timeout.clone()),
filter: defaults.timeout.is_none() || self.worker,
message: "timeout",
},
])
.map(|v| v.to_string());
self.running_metrics = self.get_value(vec![
GooseValue {
value: self.running_metrics,
filter: self.running_metrics.is_none(),
message: "running_metrics",
},
GooseValue {
value: defaults.running_metrics,
filter: defaults.running_metrics.is_none() || self.worker,
message: "running_metrics",
},
]);
self.no_reset_metrics = self
.get_value(vec![
GooseValue {
value: Some(self.no_reset_metrics),
filter: !self.no_reset_metrics,
message: "no_reset_metrics",
},
GooseValue {
value: defaults.no_reset_metrics,
filter: defaults.no_reset_metrics.is_none() || self.worker,
message: "no_reset_metrics",
},
])
.unwrap_or(false);
self.no_metrics = self
.get_value(vec![
GooseValue {
value: Some(self.no_metrics),
filter: !self.no_metrics,
message: "no_metrics",
},
GooseValue {
value: defaults.no_metrics,
filter: defaults.no_metrics.is_none() || self.worker,
message: "no_metrics",
},
])
.unwrap_or(false);
self.no_task_metrics = self
.get_value(vec![
GooseValue {
value: Some(self.no_task_metrics),
filter: !self.no_task_metrics,
message: "no_task_metrics",
},
GooseValue {
value: defaults.no_task_metrics,
filter: defaults.no_task_metrics.is_none() || self.worker,
message: "no_task_metrics",
},
])
.unwrap_or(false);
self.no_error_summary = self
.get_value(vec![
GooseValue {
value: Some(self.no_error_summary),
filter: !self.no_error_summary,
message: "no_error_summary",
},
GooseValue {
value: defaults.no_error_summary,
filter: defaults.no_error_summary.is_none() || self.worker,
message: "no_error_summary",
},
])
.unwrap_or(false);
self.report_file = match self.get_value(vec![
GooseValue {
value: Some(self.report_file.to_string()),
filter: self.report_file.is_empty(),
message: "report_file",
},
GooseValue {
value: defaults.report_file.clone(),
filter: defaults.report_file.is_none() || self.manager,
message: "report_file",
},
]) {
Some(v) => v,
None => "".to_string(),
};
self.no_debug_body = self
.get_value(vec![
GooseValue {
value: Some(self.no_debug_body),
filter: !self.no_debug_body,
message: "no_debug_body",
},
GooseValue {
value: defaults.no_debug_body,
filter: defaults.no_debug_body.is_none() || self.manager,
message: "no_debug_body",
},
])
.unwrap_or(false);
self.status_codes = self
.get_value(vec![
GooseValue {
value: Some(self.status_codes),
filter: !self.status_codes,
message: "status_codes",
},
GooseValue {
value: defaults.status_codes,
filter: defaults.status_codes.is_none() || self.worker,
message: "status_codes",
},
])
.unwrap_or(false);
self.no_telnet = self
.get_value(vec![
GooseValue {
value: Some(self.no_telnet),
filter: !self.no_telnet,
message: "no_telnet",
},
GooseValue {
value: Some(true),
filter: !self.manager && !self.worker,
message: "",
},
GooseValue {
value: defaults.no_telnet,
filter: defaults.no_telnet.is_none(),
message: "",
},
])
.unwrap_or(false);
self.no_websocket = self
.get_value(vec![
GooseValue {
value: Some(self.no_websocket),
filter: !self.no_websocket,
message: "no_websocket",
},
GooseValue {
value: Some(true),
filter: !self.manager && !self.worker,
message: "",
},
GooseValue {
value: defaults.no_websocket,
filter: defaults.no_websocket.is_none(),
message: "",
},
])
.unwrap_or(false);
self.no_autostart = self
.get_value(vec![
GooseValue {
value: Some(self.no_autostart),
filter: !self.no_autostart,
message: "no_autostart",
},
GooseValue {
value: defaults.no_autostart,
filter: defaults.no_autostart.is_none() || self.worker,
message: "no_autostart",
},
])
.unwrap_or(false);
self.no_gzip = self
.get_value(vec![
GooseValue {
value: Some(self.no_gzip),
filter: !self.no_gzip,
message: "no_gzip",
},
GooseValue {
value: defaults.no_gzip,
filter: defaults.no_gzip.is_none() || self.worker,
message: "no_gzip",
},
])
.unwrap_or(false);
self.co_mitigation = self.get_value(vec![
GooseValue {
value: self.co_mitigation.clone(),
filter: self.co_mitigation.is_none(),
message: "co_mitigation",
},
GooseValue {
value: defaults.co_mitigation.clone(),
filter: defaults.co_mitigation.is_none() || self.worker,
message: "co_mitigation",
},
GooseValue {
value: Some(GooseCoordinatedOmissionMitigation::Disabled),
filter: self.worker,
message: "",
},
]);
self.throttle_requests = self
.get_value(vec![
GooseValue {
value: Some(self.throttle_requests),
filter: self.throttle_requests == 0,
message: "throttle_requests",
},
GooseValue {
value: defaults.throttle_requests,
filter: defaults.throttle_requests.is_none() || self.manager,
message: "throttle_requests",
},
])
.unwrap_or(0);
self.sticky_follow = self
.get_value(vec![
GooseValue {
value: Some(self.sticky_follow),
filter: !self.sticky_follow,
message: "sticky_follow",
},
GooseValue {
value: defaults.sticky_follow,
filter: defaults.sticky_follow.is_none() || self.worker,
message: "sticky_follow",
},
])
.unwrap_or(false);
self.expect_workers = self.get_value(vec![
GooseValue {
value: self.expect_workers,
filter: self.expect_workers.is_none(),
message: "expect_workers",
},
GooseValue {
value: defaults.expect_workers,
filter: !self.expect_workers.is_some() && self.worker,
message: "expect_workers",
},
]);
self.no_hash_check = self
.get_value(vec![
GooseValue {
value: Some(self.no_hash_check),
filter: !self.no_hash_check,
message: "no_hash_check",
},
GooseValue {
value: defaults.no_hash_check,
filter: defaults.no_hash_check.is_none() || self.worker,
message: "no_hash_check",
},
])
.unwrap_or(false);
self.manager_bind_host = self
.get_value(vec![
GooseValue {
value: Some(self.manager_bind_host.to_string()),
filter: self.manager_bind_host.is_empty(),
message: "manager_bind_host",
},
GooseValue {
value: defaults.manager_bind_host.clone(),
filter: defaults.manager_bind_host.is_none() || !self.manager,
message: "manager_bind_host",
},
GooseValue {
value: Some("0.0.0.0".to_string()),
filter: !self.manager,
message: "manager_bind_host",
},
])
.unwrap_or_else(|| "".to_string());
self.manager_bind_port = self
.get_value(vec![
GooseValue {
value: Some(self.manager_bind_port),
filter: self.manager_bind_port == 0,
message: "manager_bind_port",
},
GooseValue {
value: defaults.manager_bind_port,
filter: defaults.manager_bind_port.is_none() || !self.manager,
message: "manager_bind_port",
},
GooseValue {
value: Some(DEFAULT_PORT.to_string().parse().unwrap()),
filter: !self.manager,
message: "manager_bind_port",
},
])
.unwrap_or(0);
self.manager_host = self
.get_value(vec![
GooseValue {
value: Some(self.manager_host.to_string()),
filter: self.manager_host.is_empty(),
message: "manager_host",
},
GooseValue {
value: defaults.manager_host.clone(),
filter: defaults.manager_host.is_none() || !self.worker,
message: "manager_host",
},
GooseValue {
value: Some("127.0.0.1".to_string()),
filter: !self.worker,
message: "manager_host",
},
])
.unwrap_or_else(|| "".to_string());
self.manager_port = self
.get_value(vec![
GooseValue {
value: Some(self.manager_port),
filter: self.manager_port == 0,
message: "manager_port",
},
GooseValue {
value: defaults.manager_port,
filter: defaults.manager_port.is_none() || !self.worker,
message: "manager_port",
},
GooseValue {
value: Some(DEFAULT_PORT.to_string().parse().unwrap()),
filter: !self.worker,
message: "manager_port",
},
])
.unwrap_or(0);
}
pub(crate) fn validate(&self) -> Result<(), GooseError> {
if self.manager {
if self.worker {
return Err(GooseError::InvalidOption {
option: "`configuration.manager` && `configuration.worker`".to_string(),
value: "true".to_string(),
detail: "Goose can not run as both Manager and Worker".to_string(),
});
} else if !self.debug_log.is_empty() {
return Err(GooseError::InvalidOption {
option: "`configuration.debug_log`".to_string(),
value: self.debug_log.clone(),
detail: "`configuration.debug_log` can not be set on the Manager.".to_string(),
});
} else if !self.error_log.is_empty() {
return Err(GooseError::InvalidOption {
option: "`configuration.error_log`".to_string(),
value: self.error_log.clone(),
detail: "`configuration.error_log` can not be set on the Manager.".to_string(),
});
} else if !self.request_log.is_empty() {
return Err(GooseError::InvalidOption {
option: "`configuration.request_log`".to_string(),
value: self.request_log.clone(),
detail: "`configuration.request_log` can not be set on the Manager."
.to_string(),
});
} else if !self.task_log.is_empty() {
return Err(GooseError::InvalidOption {
option: "`configuration.task_log`".to_string(),
value: self.request_log.clone(),
detail: "`configuration.task_log` can not be set on the Manager.".to_string(),
});
} else if self.no_autostart {
return Err(GooseError::InvalidOption {
option: "`configuration.no_autostart`".to_string(),
value: true.to_string(),
detail: "`configuration.no_autostart` can not be set on the Manager."
.to_string(),
});
} else if !self.report_file.is_empty() {
return Err(GooseError::InvalidOption {
option: "`configuration.report_file`".to_string(),
value: self.report_file.to_string(),
detail: "`configuration.report_file` can not be set on the Manager."
.to_string(),
});
} else if self.no_debug_body {
return Err(GooseError::InvalidOption {
option: "`configuration.no_debug_body`".to_string(),
value: true.to_string(),
detail: "`configuration.no_debug_body` can not be set on the Manager."
.to_string(),
});
} else if self.throttle_requests > 0 {
return Err(GooseError::InvalidOption {
option: "`configuration.throttle_requests`".to_string(),
value: self.throttle_requests.to_string(),
detail: "`configuration.throttle_requests` can not be set on the Manager."
.to_string(),
});
}
if let Some(expect_workers) = self.expect_workers.as_ref() {
if expect_workers == &0 {
return Err(GooseError::InvalidOption {
option: "`configuration.expect_workers`".to_string(),
value: expect_workers.to_string(),
detail: "`configuration.expect_workers must be set to at least 1."
.to_string(),
});
}
if let Some(users) = self.users.as_ref() {
if expect_workers > users {
return Err(GooseError::InvalidOption {
option: "`configuration.expect_workers`".to_string(),
value: expect_workers.to_string(),
detail: "`configuration.expect_workers can not be set to a value larger than `configuration.users`.".to_string(),
});
}
} else {
return Err(GooseError::InvalidOption {
option: "`configuration.expect_workers`".to_string(),
value: expect_workers.to_string(),
detail: "`configuration.expect_workers can not be set without setting `configuration.users`.".to_string(),
});
}
} else {
return Err(GooseError::InvalidOption {
option: "configuration.manager".to_string(),
value: true.to_string(),
detail: "Manager mode requires --expect-workers be configured".to_string(),
});
}
} else {
if let Some(expect_workers) = self.expect_workers.as_ref() {
return Err(GooseError::InvalidOption {
option: "`configuration.expect_workers`".to_string(),
value: expect_workers.to_string(),
detail: "`configuration.expect_workers` can not be set unless on the Manager."
.to_string(),
});
}
}
if self.worker {
if self.users.is_some() {
return Err(GooseError::InvalidOption {
option: "configuration.users".to_string(),
value: self.users.as_ref().unwrap().to_string(),
detail: "`configuration.users` can not be set together with the `configuration.worker`.".to_string(),
});
} else if self.startup_time != "0" {
return Err(GooseError::InvalidOption {
option: "`configuration.startup_time".to_string(),
value: self.startup_time.to_string(),
detail: "`configuration.startup_time` can not be set in Worker mode."
.to_string(),
});
} else if self.run_time != "0" {
return Err(GooseError::InvalidOption {
option: "`configuration.run_time".to_string(),
value: self.run_time.to_string(),
detail: "`configuration.run_time` can not be set in Worker mode.".to_string(),
});
} else if self.hatch_rate.is_some() {
return Err(GooseError::InvalidOption {
option: "`configuration.hatch_rate`".to_string(),
value: self.hatch_rate.as_ref().unwrap().to_string(),
detail: "`configuration.hatch_rate` can not be set in Worker mode.".to_string(),
});
} else if self.timeout.is_some() {
return Err(GooseError::InvalidOption {
option: "`configuration.timeout`".to_string(),
value: self.timeout.as_ref().unwrap().to_string(),
detail: "`configuration.timeout` can not be set in Worker mode.".to_string(),
});
} else if self.running_metrics.is_some() {
return Err(GooseError::InvalidOption {
option: "`configuration.running_metrics".to_string(),
value: self.running_metrics.as_ref().unwrap().to_string(),
detail: "`configuration.running_metrics` can not be set in Worker mode."
.to_string(),
});
} else if self.no_reset_metrics {
return Err(GooseError::InvalidOption {
option: "`configuration.no_reset_metrics".to_string(),
value: self.no_reset_metrics.to_string(),
detail: "`configuration.no_reset_metrics` can not be set in Worker mode."
.to_string(),
});
} else if self.no_metrics {
return Err(GooseError::InvalidOption {
option: "`configuration.no_metrics".to_string(),
value: self.no_metrics.to_string(),
detail: "`configuration.no_metrics` can not be set in Worker mode.".to_string(),
});
} else if self.no_task_metrics {
return Err(GooseError::InvalidOption {
option: "`configuration.no_task_metrics".to_string(),
value: self.no_task_metrics.to_string(),
detail: "`configuration.no_task_metrics` can not be set in Worker mode."
.to_string(),
});
} else if self.no_error_summary {
return Err(GooseError::InvalidOption {
option: "`configuration.no_error_summary".to_string(),
value: self.no_error_summary.to_string(),
detail: "`configuration.no_error_summary` can not be set in Worker mode."
.to_string(),
});
} else if self.status_codes {
return Err(GooseError::InvalidOption {
option: "`configuration.status_codes".to_string(),
value: self.status_codes.to_string(),
detail: "`configuration.status_codes` can not be set in Worker mode."
.to_string(),
});
} else if self.no_autostart {
return Err(GooseError::InvalidOption {
option: "`configuration.no_autostart`".to_string(),
value: true.to_string(),
detail: "`configuration.no_autostart` can not be set in Worker mode."
.to_string(),
});
} else if self.no_gzip {
return Err(GooseError::InvalidOption {
option: "`configuration.no_gzip`".to_string(),
value: true.to_string(),
detail: "`configuration.no_gzip` can not be set in Worker mode.".to_string(),
});
} else if self
.co_mitigation
.as_ref()
.unwrap_or(&GooseCoordinatedOmissionMitigation::Disabled)
!= &GooseCoordinatedOmissionMitigation::Disabled
{
return Err(GooseError::InvalidOption {
option: "`configuration.co_mitigation`".to_string(),
value: format!("{:?}", self.co_mitigation.as_ref().unwrap()),
detail: "`configuration.co_mitigation` can not be set in Worker mode."
.to_string(),
});
} else if !self.manager_bind_host.is_empty() {
return Err(GooseError::InvalidOption {
option: "`configuration.manager_bind_host`".to_string(),
value: self.manager_bind_host.to_string(),
detail: "`configuration.manager_bind_host` can not be set in Worker mode."
.to_string(),
});
} else if self.manager_bind_port > 0 {
return Err(GooseError::InvalidOption {
option: "`configuration.manager_bind_port`".to_string(),
value: self.manager_bind_host.to_string(),
detail: "`configuration.manager_bind_port` can not be set in Worker mode."
.to_string(),
});
} else if self.manager_host.is_empty() {
return Err(GooseError::InvalidOption {
option: "`configuration.manager_host`".to_string(),
value: self.manager_host.clone(),
detail: "`configuration.manager_host` must be set when in Worker mode."
.to_string(),
});
} else if self.manager_port == 0 {
return Err(GooseError::InvalidOption {
option: "`configuration.manager_port`".to_string(),
value: self.manager_port.to_string(),
detail: "`configuration.manager_port` must be set when in Worker mode."
.to_string(),
});
} else if self.sticky_follow {
return Err(GooseError::InvalidOption {
option: "`configuration.sticky_follow`".to_string(),
value: true.to_string(),
detail: "`configuration.sticky_follow` can not be set in Worker mode."
.to_string(),
});
} else if self.no_hash_check {
return Err(GooseError::InvalidOption {
option: "`configuration.no_hash_check`".to_string(),
value: true.to_string(),
detail: "`configuration.no_hash_check` can not be set in Worker mode."
.to_string(),
});
}
}
if let Some(hatch_rate) = self.hatch_rate.as_ref() {
if hatch_rate == "0" {
return Err(GooseError::InvalidOption {
option: "`configuration.hatch_rate`".to_string(),
value: hatch_rate.to_string(),
detail: "`configuration.hatch_rate` must be set to at least 1.".to_string(),
});
}
}
if let Some(timeout) = self.timeout.as_ref() {
if crate::util::get_float_from_string(self.timeout.clone())
.expect("failed to re-convert string to float")
<= 0.0
{
return Err(GooseError::InvalidOption {
option: "`configuration.timeout`".to_string(),
value: timeout.to_string(),
detail: "`configuration.timeout` must be greater than 0.".to_string(),
});
}
}
if let Some(users) = self.users.as_ref() {
if users == &0 {
return Err(GooseError::InvalidOption {
option: "configuration.users".to_string(),
value: users.to_string(),
detail: "`configuration.users` must be set to at least 1.".to_string(),
});
}
}
if self.startup_time != "0" {
if self.hatch_rate.is_some() {
return Err(GooseError::InvalidOption {
option: "`configuration.startup_time`".to_string(),
value: self.startup_time.to_string(),
detail: "`configuration.startup_time` can not be set with `configuration.hatch_rate`.".to_string(),
});
}
if let Some(users) = self.users.as_ref() {
if users < &2 {
return Err(GooseError::InvalidOption {
option: "configuration.users".to_string(),
value: users.to_string(),
detail: "`configuration.users` must be set to at least 2 when `configuration.startup_time` is set.".to_string(),
});
}
}
}
if self.no_metrics {
if self.status_codes {
return Err(GooseError::InvalidOption {
option: "`configuration.no_metrics`".to_string(),
value: true.to_string(),
detail: "`configuration.no_metrics` can not be set with `configuration.status_codes`.".to_string(),
});
} else if !self.request_log.is_empty() {
return Err(GooseError::InvalidOption {
option: "`configuration.request_log`".to_string(),
value: self.request_log.to_string(),
detail: "`configuration.request_log` can not be set with `configuration.no_metrics`.".to_string(),
});
} else if !self.task_log.is_empty() {
return Err(GooseError::InvalidOption {
option: "`configuration.task_log`".to_string(),
value: self.task_log.to_string(),
detail:
"`configuration.task_log` can not be set with `configuration.no_metrics`."
.to_string(),
});
} else if !self.error_log.is_empty() {
return Err(GooseError::InvalidOption {
option: "`configuration.error_log`".to_string(),
value: self.error_log.to_string(),
detail:
"`configuration.error_log` can not be set with `configuration.no_metrics`."
.to_string(),
});
} else if !self.report_file.is_empty() {
return Err(GooseError::InvalidOption {
option: "`configuration.report_file`".to_string(),
value: self.report_file.to_string(),
detail:
"`configuration.report_file` can not be set with `configuration.no_metrics`."
.to_string(),
});
} else if self.co_mitigation.as_ref().unwrap()
!= &GooseCoordinatedOmissionMitigation::Disabled
{
return Err(GooseError::InvalidOption {
option: "`configuration.co_mitigation`".to_string(),
value: format!("{:?}", self.co_mitigation.as_ref().unwrap()),
detail: "`configuration.co_mitigation` can not be set with `configuration.no_metrircs`."
.to_string(),
});
}
}
if self.no_autostart && self.no_telnet && self.no_websocket {
return Err(GooseError::InvalidOption {
option: "`configuration.no_autostart`".to_string(),
value: true.to_string(),
detail: "`configuration.no_autostart` requires at least one Controller be enabled"
.to_string(),
});
}
if self.throttle_requests > 0 {
if self.throttle_requests == 0 {
return Err(GooseError::InvalidOption {
option: "`configuration.throttle_requests`".to_string(),
value: self.throttle_requests.to_string(),
detail: "`configuration.throttle_requests` must be set to at least 1 request per second.".to_string(),
});
} else if self.throttle_requests > 1_000_000 {
return Err(GooseError::InvalidOption {
option: "`configuration.throttle_requests`".to_string(),
value: self.throttle_requests.to_string(),
detail: "`configuration.throttle_requests` can not be set to more than 1,000,000 request per second.".to_string(),
});
}
}
Ok(())
}
pub(crate) fn initialize_goose_logger(&self) {
let debug_level = match self.verbose {
0 => LevelFilter::Warn,
1 => LevelFilter::Info,
2 => LevelFilter::Debug,
_ => LevelFilter::Trace,
};
let log_level = match self.log_level {
0 => LevelFilter::Warn,
1 => LevelFilter::Info,
2 => LevelFilter::Debug,
_ => LevelFilter::Trace,
};
let goose_log: Option<PathBuf> = if !self.goose_log.is_empty() {
Some(PathBuf::from(&self.goose_log))
} else {
None
};
if let Some(log_to_file) = goose_log {
match CombinedLogger::init(vec![
SimpleLogger::new(debug_level, Config::default()),
WriteLogger::new(
log_level,
Config::default(),
std::fs::File::create(&log_to_file).unwrap(),
),
]) {
Ok(_) => (),
Err(e) => {
info!("failed to initialize CombinedLogger: {}", e);
}
}
info!("Writing to log file: {}", log_to_file.display());
} else {
match CombinedLogger::init(vec![SimpleLogger::new(debug_level, Config::default())]) {
Ok(_) => (),
Err(e) => {
info!("failed to initialize CombinedLogger: {}", e);
}
}
}
info!("Output verbosity level: {}", debug_level);
info!("Logfile verbosity level: {}", log_level);
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn set_defaults() {
let host = "http://example.com/".to_string();
let users: usize = 10;
let run_time: usize = 10;
let hatch_rate = "2".to_string();
let timeout = "45".to_string();
let log_level: usize = 1;
let goose_log = "custom-goose.log".to_string();
let verbose: usize = 0;
let report_file = "custom-goose-report.html".to_string();
let request_log = "custom-goose-request.log".to_string();
let task_log = "custom-goose-task.log".to_string();
let debug_log = "custom-goose-debug.log".to_string();
let error_log = "custom-goose-error.log".to_string();
let throttle_requests: usize = 25;
let expect_workers: usize = 5;
let manager_bind_host = "127.0.0.1".to_string();
let manager_bind_port: usize = 1221;
let manager_host = "127.0.0.1".to_string();
let manager_port: usize = 1221;
let goose_attack = GooseAttack::initialize()
.unwrap()
.set_default(GooseDefault::Host, host.as_str())
.unwrap()
.set_default(GooseDefault::Users, users)
.unwrap()
.set_default(GooseDefault::RunTime, run_time)
.unwrap()
.set_default(GooseDefault::HatchRate, hatch_rate.as_str())
.unwrap()
.set_default(GooseDefault::LogLevel, log_level)
.unwrap()
.set_default(GooseDefault::GooseLog, goose_log.as_str())
.unwrap()
.set_default(GooseDefault::Verbose, verbose)
.unwrap()
.set_default(GooseDefault::Timeout, timeout.as_str())
.unwrap()
.set_default(GooseDefault::RunningMetrics, 15)
.unwrap()
.set_default(GooseDefault::NoResetMetrics, true)
.unwrap()
.set_default(GooseDefault::NoMetrics, true)
.unwrap()
.set_default(GooseDefault::NoTaskMetrics, true)
.unwrap()
.set_default(GooseDefault::NoErrorSummary, true)
.unwrap()
.set_default(GooseDefault::NoTelnet, true)
.unwrap()
.set_default(GooseDefault::NoWebSocket, true)
.unwrap()
.set_default(GooseDefault::NoAutoStart, true)
.unwrap()
.set_default(GooseDefault::NoGzip, true)
.unwrap()
.set_default(GooseDefault::ReportFile, report_file.as_str())
.unwrap()
.set_default(GooseDefault::RequestLog, request_log.as_str())
.unwrap()
.set_default(GooseDefault::RequestFormat, GooseLogFormat::Raw)
.unwrap()
.set_default(GooseDefault::RequestBody, true)
.unwrap()
.set_default(GooseDefault::TaskLog, task_log.as_str())
.unwrap()
.set_default(GooseDefault::TaskFormat, GooseLogFormat::Raw)
.unwrap()
.set_default(GooseDefault::ErrorLog, error_log.as_str())
.unwrap()
.set_default(GooseDefault::ErrorFormat, GooseLogFormat::Csv)
.unwrap()
.set_default(GooseDefault::DebugLog, debug_log.as_str())
.unwrap()
.set_default(GooseDefault::DebugFormat, GooseLogFormat::Csv)
.unwrap()
.set_default(GooseDefault::NoDebugBody, true)
.unwrap()
.set_default(GooseDefault::StatusCodes, true)
.unwrap()
.set_default(
GooseDefault::CoordinatedOmissionMitigation,
GooseCoordinatedOmissionMitigation::Disabled,
)
.unwrap()
.set_default(GooseDefault::ThrottleRequests, throttle_requests)
.unwrap()
.set_default(GooseDefault::StickyFollow, true)
.unwrap()
.set_default(GooseDefault::Manager, true)
.unwrap()
.set_default(GooseDefault::ExpectWorkers, expect_workers)
.unwrap()
.set_default(GooseDefault::NoHashCheck, true)
.unwrap()
.set_default(GooseDefault::ManagerBindHost, manager_bind_host.as_str())
.unwrap()
.set_default(GooseDefault::ManagerBindPort, manager_bind_port)
.unwrap()
.set_default(GooseDefault::Worker, true)
.unwrap()
.set_default(GooseDefault::ManagerHost, manager_host.as_str())
.unwrap()
.set_default(GooseDefault::ManagerPort, manager_port)
.unwrap();
assert!(goose_attack.defaults.host == Some(host));
assert!(goose_attack.defaults.users == Some(users));
assert!(goose_attack.defaults.run_time == Some(run_time));
assert!(goose_attack.defaults.hatch_rate == Some(hatch_rate));
assert!(goose_attack.defaults.log_level == Some(log_level as u8));
assert!(goose_attack.defaults.goose_log == Some(goose_log));
assert!(goose_attack.defaults.request_body == Some(true));
assert!(goose_attack.defaults.no_debug_body == Some(true));
assert!(goose_attack.defaults.verbose == Some(verbose as u8));
assert!(goose_attack.defaults.running_metrics == Some(15));
assert!(goose_attack.defaults.no_reset_metrics == Some(true));
assert!(goose_attack.defaults.no_metrics == Some(true));
assert!(goose_attack.defaults.no_task_metrics == Some(true));
assert!(goose_attack.defaults.no_error_summary == Some(true));
assert!(goose_attack.defaults.no_telnet == Some(true));
assert!(goose_attack.defaults.no_websocket == Some(true));
assert!(goose_attack.defaults.no_autostart == Some(true));
assert!(goose_attack.defaults.timeout == Some(timeout));
assert!(goose_attack.defaults.no_gzip == Some(true));
assert!(goose_attack.defaults.report_file == Some(report_file));
assert!(goose_attack.defaults.request_log == Some(request_log));
assert!(goose_attack.defaults.request_format == Some(GooseLogFormat::Raw));
assert!(goose_attack.defaults.error_log == Some(error_log));
assert!(goose_attack.defaults.error_format == Some(GooseLogFormat::Csv));
assert!(goose_attack.defaults.debug_log == Some(debug_log));
assert!(goose_attack.defaults.debug_format == Some(GooseLogFormat::Csv));
assert!(goose_attack.defaults.status_codes == Some(true));
assert!(
goose_attack.defaults.co_mitigation
== Some(GooseCoordinatedOmissionMitigation::Disabled)
);
assert!(goose_attack.defaults.throttle_requests == Some(throttle_requests));
assert!(goose_attack.defaults.sticky_follow == Some(true));
assert!(goose_attack.defaults.manager == Some(true));
assert!(goose_attack.defaults.expect_workers == Some(expect_workers));
assert!(goose_attack.defaults.no_hash_check == Some(true));
assert!(goose_attack.defaults.manager_bind_host == Some(manager_bind_host));
assert!(goose_attack.defaults.manager_bind_port == Some(manager_bind_port as u16));
assert!(goose_attack.defaults.worker == Some(true));
assert!(goose_attack.defaults.manager_host == Some(manager_host));
assert!(goose_attack.defaults.manager_port == Some(manager_port as u16));
}
}