diff --git a/packages/check-sql/Cargo.toml b/packages/check-sql/Cargo.toml index bf18011e91a..275e5ea821f 100644 --- a/packages/check-sql/Cargo.toml +++ b/packages/check-sql/Cargo.toml @@ -31,8 +31,9 @@ clap = { version = "4.1.*", features = [ "derive", ] } # replace with 4.4.2 if rutsc >= 1.70 yaml-rust = "0.4.5" +lazy_static = { version = "1.4" } + [dev-dependencies] -lazy_static = { version = "*" } tempfile = { version = "*" } assert_cmd = { version = "*" } diff --git a/packages/check-sql/src/config/yaml.rs b/packages/check-sql/src/config/yaml.rs index 1848756e447..65edbb3eda2 100644 --- a/packages/check-sql/src/config/yaml.rs +++ b/packages/check-sql/src/config/yaml.rs @@ -82,8 +82,16 @@ impl Get for Yaml { } pub fn load_from_file(file_name: &Path) -> Result> { - let content = read_file(file_name)?; - load_from_str(&content) + match read_file(file_name) { + Ok(content) => load_from_str(&content), + Err(e) => anyhow::bail!( + "Can't read file: {}, {e} ", + // Use relatively complicated method to print name of the file + // as it is not possible to use "{file_name:?}": produces to many backslashes + // in Windows. Probability to NOT decode filename as UTF-8 is nil. + file_name.as_os_str().to_str().unwrap_or("") + ), + } } fn read_file(file_name: &Path) -> Result { diff --git a/packages/check-sql/src/constants.rs b/packages/check-sql/src/constants.rs index 6103c2229bc..413d92723e1 100644 --- a/packages/check-sql/src/constants.rs +++ b/packages/check-sql/src/constants.rs @@ -1,10 +1,26 @@ // Copyright (C) 2023 Checkmk GmbH - License: GNU General Public License v2 // This file is part of Checkmk (https://checkmk.com). It is subject to the terms and // conditions defined in the file COPYING, which is part of this source code package. - +use lazy_static::lazy_static; +use std::path::{Path, PathBuf}; pub mod log { use flexi_logger::{Cleanup, Criterion, Naming}; pub const FILE_MAX_SIZE: Criterion = Criterion::Size(100000); pub const FILE_NAMING: Naming = Naming::Numbers; pub const FILE_CLEANUP: Cleanup = Cleanup::KeepLogFiles(5); } + +pub mod environment { + pub const CONFIG_NAME: &str = "check-sql.yml"; + pub const CONFIG_DIR_ENV_VAR: &str = "MK_CONFDIR"; +} + +lazy_static! { + pub static ref DEFAULT_CONFIG_FILE: PathBuf = + Path::new(&get_env_value(environment::CONFIG_DIR_ENV_VAR, ".")) + .join(environment::CONFIG_NAME); +} + +fn get_env_value(var: &str, on_lack: &str) -> String { + std::env::var(var).unwrap_or(on_lack.to_string()) +} diff --git a/packages/check-sql/src/emit.rs b/packages/check-sql/src/emit.rs index 277b21fcff7..6a665810ca5 100644 --- a/packages/check-sql/src/emit.rs +++ b/packages/check-sql/src/emit.rs @@ -2,13 +2,15 @@ // This file is part of Checkmk (https://checkmk.com). It is subject to the terms and // conditions defined in the file COPYING, which is part of this source code package. +const PREFIX: &str = "mssql"; + pub fn header(name: &str, separator: Option) -> String { match separator { Some(s) => { let sep = s as u8; - format!("<<<{name}:sep({sep:0>2})>>>\n") + format!("<<<{PREFIX}_{name}:sep({sep:0>2})>>>\n") } - None => format!("<<<{name}>>>\n"), + None => format!("<<<{PREFIX}_{name}>>>\n"), } } @@ -17,9 +19,9 @@ mod test { use super::*; #[test] fn test_header() { - assert_eq!(header("name", Some('\n')), "<<>>\n"); - assert_eq!(header("name", Some('\t')), "<<>>\n"); - assert_eq!(header("name", Some('|')), "<<>>\n"); - assert_eq!(header("name", None), "<<>>\n"); + assert_eq!(header("name", Some('\n')), "<<>>\n"); + assert_eq!(header("name", Some('\t')), "<<>>\n"); + assert_eq!(header("name", Some('|')), "<<>>\n"); + assert_eq!(header("name", None), "<<>>\n"); } } diff --git a/packages/check-sql/src/ms_sql/api.rs b/packages/check-sql/src/ms_sql/api.rs index db64efdb586..cdaa09b470a 100644 --- a/packages/check-sql/src/ms_sql/api.rs +++ b/packages/check-sql/src/ms_sql/api.rs @@ -54,8 +54,8 @@ fn to_section(name: &String) -> Section { fn get_section_separator(name: &str) -> Option { match name { - "instance" | "database" | "counters" | "blocked_sessions" | "transactionlogs" - | "datafiles" | "cluster" => Some('|'), + "instance" | "databases" | "counters" | "blocked_sessions" | "transactionlogs" + | "datafiles" | "cluster" | "clusters" | "backup" => Some('|'), "jobs" | "mirroring" | "availability_groups" => Some('\t'), "tablespaces" | "connections" => None, _ => None, diff --git a/packages/check-sql/src/setup.rs b/packages/check-sql/src/setup.rs index 674254bcc9f..605ab275d81 100644 --- a/packages/check-sql/src/setup.rs +++ b/packages/check-sql/src/setup.rs @@ -37,10 +37,12 @@ fn init_logging_from_args(args: &Args) -> Result<()> { } fn get_check_config(args: &Args) -> Result { - match args.config_file { - Some(ref config_file) => Ok(CheckConfig::load_file(config_file)?), - None => Ok(CheckConfig::default()), - } + let file = match args.config_file { + Some(ref config_file) => config_file, + None => &constants::DEFAULT_CONFIG_FILE, + }; + + CheckConfig::load_file(file) } fn init_logging( diff --git a/packages/check-sql/tests/common/mod.rs b/packages/check-sql/tests/common/mod.rs new file mode 100644 index 00000000000..7389507f245 --- /dev/null +++ b/packages/check-sql/tests/common/mod.rs @@ -0,0 +1,5 @@ +// Copyright (C) 2023 Checkmk GmbH - License: GNU General Public License v2 +// This file is part of Checkmk (https://checkmk.com). It is subject to the terms and +// conditions defined in the file COPYING, which is part of this source code package. + +pub mod tools; diff --git a/packages/check-sql/tests/tools.rs b/packages/check-sql/tests/common/tools.rs similarity index 56% rename from packages/check-sql/tests/tools.rs rename to packages/check-sql/tests/common/tools.rs index 0d22910216e..76c55daf20e 100644 --- a/packages/check-sql/tests/tools.rs +++ b/packages/check-sql/tests/common/tools.rs @@ -2,14 +2,39 @@ // This file is part of Checkmk (https://checkmk.com). It is subject to the terms and // conditions defined in the file COPYING, which is part of this source code package. +#[cfg(windows)] +use anyhow::Result; +#[cfg(windows)] +use assert_cmd::output::OutputError; use assert_cmd::Command; use std::io::Write; +#[cfg(windows)] +use std::path::Path; +#[cfg(windows)] +use std::process::Output; use tempfile::NamedTempFile; +#[cfg(windows)] +use tempfile::{Builder, TempDir}; pub fn run_bin() -> Command { Command::cargo_bin("check-sql").unwrap() } +/// returns stderr content + resulting code +#[cfg(windows)] +pub fn get_bad_results(output_err: &OutputError) -> Result<(String, i32)> { + let output = output_err.as_output().unwrap(); + let stderr = std::str::from_utf8(&output.stderr).map(|s| s.to_string()); + Ok((stderr?, output.status.code().unwrap())) +} + +/// returns stdout content + resulting code +#[cfg(windows)] +pub fn get_good_results(output: &Output) -> Result<(String, i32)> { + let stdout = std::str::from_utf8(&output.stdout).map(|s| s.to_string()); + Ok((stdout?, output.status.code().unwrap())) +} + pub const SQL_DB_ENDPOINT: &str = "CI_TEST_SQL_DB_ENDPOINT"; const SQL_DB_ENDPOINT_SPLITTER: char = ':'; pub struct SqlDbEndpoint { @@ -39,6 +64,32 @@ pub fn get_remote_sql_from_env_var() -> Option { None } +#[cfg(windows)] +fn create_temp_dir_with_file(file_name: &str, content: &str) -> TempDir { + let dir = create_temp_process_dir(); + create_file_with_content(dir.path(), file_name, content); + + dir +} + +#[cfg(windows)] +pub fn create_file_with_content(dir: &Path, file_name: &str, content: &str) { + let file_path = dir.join(file_name); + let mut file = std::fs::File::create(file_path).unwrap(); + file.write_all(content.as_bytes()).unwrap(); +} + +#[cfg(windows)] +fn create_temp_process_dir() -> TempDir { + let dir = Builder::new() + .prefix(&format!("check-sql-{}", std::process::id())) + .rand_bytes(5) + .tempdir() + .unwrap(); + + dir +} + pub fn create_remote_config(end_point: &SqlDbEndpoint) -> NamedTempFile { let mut l = NamedTempFile::new().unwrap(); let config = format!( @@ -58,6 +109,7 @@ mssql: l.write_all(config.as_bytes()).unwrap(); l } + pub fn create_local_config() -> NamedTempFile { let mut l = NamedTempFile::new().unwrap(); let config = r#" @@ -73,3 +125,8 @@ mssql: l.write_all(config.as_bytes()).unwrap(); l } + +#[cfg(windows)] +pub fn create_config_dir_with_yml(content: &str) -> TempDir { + create_temp_dir_with_file("check-sql.yml", content) +} diff --git a/packages/check-sql/tests/test_ms_sql.rs b/packages/check-sql/tests/test_ms_sql.rs index 8af30adac6a..0317de3ccc5 100644 --- a/packages/check-sql/tests/test_ms_sql.rs +++ b/packages/check-sql/tests/test_ms_sql.rs @@ -2,9 +2,11 @@ // This file is part of Checkmk (https://checkmk.com). It is subject to the terms and // conditions defined in the file COPYING, which is part of this source code package. -mod tools; - +mod common; use check_sql::{config::CheckConfig, ms_sql::api}; +use common::tools; +#[cfg(windows)] +use tempfile::TempDir; #[cfg(windows)] #[tokio::test(flavor = "multi_thread")] async fn test_local_connection() { @@ -73,7 +75,7 @@ fn test_run_remote() { #[cfg(windows)] #[test] -fn test_run_local_as_plugin_fail() { +fn test_run_local_as_plugin_without_config() { assert!( tools::run_bin() .unwrap_err() @@ -83,4 +85,67 @@ fn test_run_local_as_plugin_fail() { .code() == Some(1) ); + assert!( + tools::run_bin() + .env("MK_CONFDIR", ".") + .unwrap_err() + .as_output() + .unwrap() + .status + .code() + == Some(1) + ); +} + +#[cfg(windows)] +#[test] +fn test_run_local_as_plugin_with_config() { + // Good config + let content = r#" +--- +mssql: + standard: + authentication: + username: "nobody" + type: "integrated" + connection: + hostname: "localhost" +"#; + let dir = tools::create_config_dir_with_yml(content); + let exec = tools::run_bin() + .env("MK_CONFDIR", dir.path()) + .timeout(std::time::Duration::from_secs(5)) + .unwrap(); + let (stdout, code) = tools::get_good_results(&exec).unwrap(); + assert_eq!(code, 0); + assert!(stdout.starts_with( + r"<<>> +<<>> +<<>> +<<>> +<<>> +<<>> +<<>> +<<>> +<<>> +<<>> +<<>> +<<>> +<<>>" + )); + + // Bad config + invalidate_config_in_dir(&dir); + let exec_err = tools::run_bin() + .env("MK_CONFDIR", dir.path()) + .timeout(std::time::Duration::from_secs(5)) + .unwrap_err(); + let (stderr, code) = tools::get_bad_results(&exec_err).unwrap(); + assert_eq!(code, 1); + assert_eq!(stderr, "Error: No Config\n"); +} + +#[cfg(windows)] +fn invalidate_config_in_dir(dir: &TempDir) { + tools::create_file_with_content(dir.path(), "check-sql.yml", "---\n"); }