use crate::command_config::CommandConfig;
use crate::errors::{AnsibleError, Result};
use std::fmt::{Display, Formatter};
use std::process;
#[derive(Debug, Clone)]
pub struct AnsibleConfig {
pub(crate) command: String,
pub(crate) cfg: CommandConfig,
pub(crate) config_file: Option<String>,
pub(crate) plugin_type: Option<String>,
}
impl Default for AnsibleConfig {
fn default() -> Self {
Self {
command: "ansible-config".into(),
cfg: CommandConfig::default(),
config_file: None,
plugin_type: None,
}
}
}
impl Display for AnsibleConfig {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.command)?;
if let Some(ref config_file) = self.config_file {
write!(f, " --config {}", config_file)?;
}
if let Some(ref plugin_type) = self.plugin_type {
write!(f, " --type {}", plugin_type)?;
}
if !self.cfg.args.is_empty() {
write!(f, " {}", self.cfg.args.join(" "))?;
}
Ok(())
}
}
impl AnsibleConfig {
pub fn new() -> Self {
Self::default()
}
pub fn set_config_file(&mut self, file_path: impl Into<String>) -> &mut Self {
self.config_file = Some(file_path.into());
self
}
pub fn set_format(&mut self, format: ConfigFormat) -> &mut Self {
self.arg("--format").arg(format.to_string());
self
}
pub fn set_plugin_type(&mut self, plugin_type: PluginType) -> &mut Self {
self.plugin_type = Some(plugin_type.to_string());
self
}
pub fn arg(&mut self, arg: impl Into<String>) -> &mut Self {
self.cfg.arg(arg.into());
self
}
pub fn args<I, S>(&mut self, args: I) -> &mut Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
let args_vec: Vec<String> = args.into_iter().map(|s| s.into()).collect();
self.cfg.args(args_vec);
self
}
pub fn set_system_envs(&mut self) -> &mut Self {
self.cfg.set_system_envs();
self
}
pub fn add_env(&mut self, key: impl Into<String>, value: impl Into<String>) -> &mut Self {
self.cfg.add_env(key, value);
self
}
fn execute_config_command(&self, action: &str, args: &[String]) -> Result<String> {
let mut cmd = process::Command::new(&self.command);
cmd.envs(&self.cfg.envs);
cmd.arg(action);
if let Some(ref config_file) = self.config_file {
cmd.args(["--config", config_file]);
}
if let Some(ref plugin_type) = self.plugin_type {
cmd.args(["--type", plugin_type]);
}
cmd.args(&self.cfg.args);
cmd.args(args);
let output = cmd.output()?;
if !output.status.success() {
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
return Err(AnsibleError::command_failed(
format!("Ansible config {} command failed", action),
output.status.code(),
Some(stdout),
Some(stderr),
));
}
let result = [output.stdout, "\n".as_bytes().to_vec(), output.stderr].concat();
let s = String::from_utf8_lossy(&result);
Ok(s.to_string())
}
pub fn list(&self) -> Result<String> {
self.execute_config_command("list", &[])
}
pub fn list_with_format(&self, format: ConfigFormat) -> Result<String> {
self.execute_config_command("list", &[
"--format".to_string(),
format.to_string(),
])
}
pub fn dump(&self) -> Result<String> {
self.execute_config_command("dump", &[])
}
pub fn dump_with_format(&self, format: ConfigFormat) -> Result<String> {
self.execute_config_command("dump", &[
"--format".to_string(),
format.to_string(),
])
}
pub fn dump_changed_only(&self) -> Result<String> {
self.execute_config_command("dump", &["--only-changed".to_string()])
}
pub fn view(&self) -> Result<String> {
self.execute_config_command("view", &[])
}
pub fn init(&self) -> Result<String> {
self.execute_config_command("init", &[])
}
pub fn init_with_format(&self, format: ConfigFormat) -> Result<String> {
self.execute_config_command("init", &[
"--format".to_string(),
format.to_string(),
])
}
pub fn init_disabled(&self) -> Result<String> {
self.execute_config_command("init", &["--disabled".to_string()])
}
pub fn validate(&self) -> Result<String> {
self.execute_config_command("validate", &[])
}
pub fn validate_with_format(&self, format: ConfigFormat) -> Result<String> {
self.execute_config_command("validate", &[
"--format".to_string(),
format.to_string(),
])
}
pub fn verbose(&mut self) -> &mut Self {
self.cfg.arg("-v");
self
}
pub fn verbosity(&mut self, level: u8) -> &mut Self {
let v_arg = "-".to_string() + &"v".repeat(level as usize);
self.cfg.arg(v_arg);
self
}
pub fn get_config(&self) -> &CommandConfig {
&self.cfg
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConfigFormat {
Json,
Yaml,
Display,
}
impl Display for ConfigFormat {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ConfigFormat::Json => write!(f, "json"),
ConfigFormat::Yaml => write!(f, "yaml"),
ConfigFormat::Display => write!(f, "display"),
}
}
}
impl ConfigFormat {
pub fn all() -> Vec<ConfigFormat> {
vec![ConfigFormat::Json, ConfigFormat::Yaml, ConfigFormat::Display]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PluginType {
Become,
Cache,
Callback,
Connection,
Httpapi,
Inventory,
Lookup,
Netconf,
Shell,
Strategy,
Vars,
}
impl Display for PluginType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
PluginType::Become => write!(f, "become"),
PluginType::Cache => write!(f, "cache"),
PluginType::Callback => write!(f, "callback"),
PluginType::Connection => write!(f, "connection"),
PluginType::Httpapi => write!(f, "httpapi"),
PluginType::Inventory => write!(f, "inventory"),
PluginType::Lookup => write!(f, "lookup"),
PluginType::Netconf => write!(f, "netconf"),
PluginType::Shell => write!(f, "shell"),
PluginType::Strategy => write!(f, "strategy"),
PluginType::Vars => write!(f, "vars"),
}
}
}
impl PluginType {
pub fn all() -> Vec<PluginType> {
vec![
PluginType::Become,
PluginType::Cache,
PluginType::Callback,
PluginType::Connection,
PluginType::Httpapi,
PluginType::Inventory,
PluginType::Lookup,
PluginType::Netconf,
PluginType::Shell,
PluginType::Strategy,
PluginType::Vars,
]
}
}