Skip to content

Commit

Permalink
WIP: feat: Add pacman module
Browse files Browse the repository at this point in the history
  • Loading branch information
pando85 committed Jul 17, 2023
1 parent 57aab5e commit f0a342d
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 17 deletions.
11 changes: 11 additions & 0 deletions examples/install_arch_packages.rh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env rash

- name: Install Rust dependencies
pacman:
executable: "{{ rash.dir }}/../rash_core/tests/mocks/pacman.rh"
extra_args: "--nodeps --nodeps"
force: true
name:
- rustup
- bpftrace
state: latest
2 changes: 1 addition & 1 deletion examples/task.rh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
- debug:
var: "find_result.extra"

- name: "save password to multiple files"
- name: save password to multiple files
copy:
content: "{{ env.MY_PASSWORD }}"
dest: "/tmp/MY_PASSWORD_FILE_{{ file_name }}"
Expand Down
3 changes: 1 addition & 2 deletions rash_core/src/modules/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
///
/// - file:
/// path: /yea
/// state: present
/// mode: 0644
/// ```
/// ANCHOR_END: examples
Expand Down Expand Up @@ -63,7 +62,7 @@ pub struct Params {
/// If _file_, even with other options (such as mode), the file will be modified if it exists but
/// will NOT be created if it does not exist.
/// If _touch_, an empty file will be created if the file does not exist.
/// **[default: file]**
/// **[default: `"file"`]**
state: Option<State>,
}

Expand Down
22 changes: 8 additions & 14 deletions rash_core/src/modules/find.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
/// ANCHOR_END: examples
use crate::error::{Error, ErrorKind, Result};
use crate::modules::{parse_if_json, parse_params, Module, ModuleResult};
use crate::modules::utils::default_false;
use crate::vars::Vars;

#[cfg(feature = "docs")]
Expand All @@ -47,15 +48,13 @@ use schemars::schema::RootSchema;
use schemars::JsonSchema;
use serde::Deserialize;
use serde_with::{serde_as, OneOrMany};
use serde_yaml::value;
use serde_yaml::Value;
use serde_yaml::{value, Value};
#[cfg(feature = "docs")]
use strum_macros::{Display, EnumString};

#[derive(Debug, PartialEq, Deserialize)]
#[derive(Default, Debug, PartialEq, Deserialize)]
#[cfg_attr(feature = "docs", derive(EnumString, Display, JsonSchema))]
#[serde(rename_all = "lowercase")]
#[derive(Default)]
enum FileType {
Any,
Directory,
Expand All @@ -68,10 +67,6 @@ fn default_file_type() -> Option<FileType> {
Some(FileType::default())
}

fn default_false() -> Option<bool> {
Some(false)
}

#[serde_as]
#[derive(Debug, PartialEq, Deserialize)]
#[cfg_attr(feature = "docs", derive(JsonSchema, DocJsonSchema))]
Expand All @@ -85,15 +80,15 @@ pub struct Params {
#[serde(default)]
excludes: Option<Vec<String>>,
/// Type of file to select.
/// **[default: file]**
/// **[default: `"file"`]**
#[serde(default = "default_file_type")]
file_type: Option<FileType>,
/// Set this to true to follow symlinks
/// **[default: false]**
/// **[default: `false`]**
#[serde(default = "default_false")]
follow: Option<bool>,
/// Set this to yes to include hidden files, otherwise they will be ignored.
/// **[default: false]**
/// **[default: `false`]**
#[serde(default = "default_false")]
hidden: Option<bool>,
/// The patterns restrict the list of files to be returned to those whose basenames
Expand All @@ -103,7 +98,7 @@ pub struct Params {
#[serde(default)]
patterns: Option<Vec<String>>,
/// If target is a directory, recursively descend into the directory looking for files.
/// **[default: false]**
/// **[default: `false`]**
#[serde(default = "default_false")]
recurse: Option<bool>,
/// Select files whose size is less than the specified size.
Expand All @@ -115,9 +110,8 @@ pub struct Params {

impl Default for Params {
fn default() -> Self {
let paths: Vec<String> = Vec::new();
Params {
paths,
paths: Vec::new(),
excludes: None,
file_type: Some(FileType::default()),
follow: Some(false),
Expand Down
4 changes: 4 additions & 0 deletions rash_core/src/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ mod copy;
mod debug;
mod file;
mod find;
mod pacman;
mod set_vars;
mod template;
mod utils;

use crate::error::{Error, ErrorKind, Result};
use crate::modules::assert::Assert;
Expand All @@ -14,6 +16,7 @@ use crate::modules::copy::Copy;
use crate::modules::debug::Debug;
use crate::modules::file::File;
use crate::modules::find::Find;
use crate::modules::pacman::Pacman;
use crate::modules::set_vars::SetVars;
use crate::modules::template::Template;
use crate::vars::Vars;
Expand Down Expand Up @@ -85,6 +88,7 @@ lazy_static! {
(Debug.get_name(), Box::new(Debug) as Box<dyn Module>),
(File.get_name(), Box::new(File) as Box<dyn Module>),
(Find.get_name(), Box::new(Find) as Box<dyn Module>),
(Pacman.get_name(), Box::new(Pacman) as Box<dyn Module>),
(SetVars.get_name(), Box::new(SetVars) as Box<dyn Module>),
(Template.get_name(), Box::new(Template) as Box<dyn Module>),
]
Expand Down
206 changes: 206 additions & 0 deletions rash_core/src/modules/pacman.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/// ANCHOR: module
/// # pacman
///
/// Manage packages with the pacman package manager, which is used by Arch Linux and its variants.
///
/// ## Attributes
///
/// ```yaml
/// check_mode:
/// support: always
/// ```
/// ANCHOR_END: module
/// ANCHOR: examples
/// ## Example
///
/// ```yaml
/// - name: Install package rustup from repo
/// pacman:
/// name: rustup
/// state: present
///
/// - pacman:
/// executable: yay
/// name:
/// - rash
/// - timer-rs
/// state: present
/// ```
/// ANCHOR_END: examples
use crate::error::{Error, ErrorKind, Result};
use crate::modules::{parse_params, Module, ModuleResult};
use crate::modules::utils::default_false;
use crate::vars::Vars;

#[cfg(feature = "docs")]
use rash_derive::DocJsonSchema;

#[cfg(feature = "docs")]
use schemars::schema::RootSchema;
#[cfg(feature = "docs")]
use schemars::JsonSchema;
use serde::Deserialize;
use serde_with::{serde_as, OneOrMany};
use serde_yaml::Value;
#[cfg(feature = "docs")]
use strum_macros::{Display, EnumString};


fn default_executable() -> Option<String> {
Some("pacman".to_string())
}

#[derive(Default, Debug, PartialEq, Deserialize)]
#[cfg_attr(feature = "docs", derive(EnumString, Display, JsonSchema))]
#[serde(rename_all = "lowercase")]
enum State {
Absent,
Installed,
Latest,
#[default]
Present,
Removed,
}

fn default_state() -> Option<State> {
Some(State::default())
}

#[serde_as]
#[derive(Debug, PartialEq, Deserialize)]
#[cfg_attr(feature = "docs", derive(JsonSchema, DocJsonSchema))]
#[serde(deny_unknown_fields)]
pub struct Params {
/// Path of the binary to use. This can either be `pacman` or a pacman compatible AUR helper.
/// **[default: `"pacman"`]**
#[serde(default = "default_executable")]
executable: Option<String>,
/// Additional option to pass to executable.
extra_args: Option<String>,
/// When removing packages, forcefully remove them, without any checks.
/// Same as extra_args=”–nodeps –nodeps”. When combined with update_cache,]
/// force a refresh of all package databases. Same as update_cache_extra_args=”–refresh –refresh”.
/// **[default: `false`]**
#[serde(default = "default_false")]
force: Option<bool>,
/// Name or list of names of the package(s) or file(s) to install, upgrade, or remove.
#[serde_as(deserialize_as = "OneOrMany<_>")]
#[serde(default)]
name: Vec<String>,
/// Whether to install (`present`, `latest`), or remove (`absent`) a package.
/// `present` will simply ensure that a desired package is installed.
/// `latest` will update the specified package if it is not of the latest available version.
/// `absent` will remove the specified package.
/// **[default: `"present"`]**
#[serde(default = "default_state")]
state: Option<State>,
}

impl Default for Params {
fn default() -> Self {
Params {
executable: Some("pacman".to_string()),
extra_args: None,
force: Some(false),
name: Vec::new(),
state: Some(State::Present),
}
}
}


#[derive(Debug)]
pub struct Pacman;


impl Module for Pacman {
fn get_name(&self) -> &str {
"pacman"
}

fn exec(&self, optional_params: Value, vars: Vars, _check_mode: bool) -> Result<(ModuleResult, Vars)> {
Ok((
pacman(parse_params(optional_params)?, &vars)?,
vars,
))
}

#[cfg(feature = "docs")]
fn get_json_schema(&self) -> Option<RootSchema> {
Some(Params::get_json_schema())
}
}

fn pacman(params: Params, vars: &Vars) -> Result<ModuleResult> {
Ok(ModuleResult {
changed: false,
output: None,
extra: None,
})
}


#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_parse_params() {
let yaml: Value = serde_yaml::from_str(
r#"
name: rustup
state: present
"#,
)
.unwrap();
let params: Params = parse_params(yaml).unwrap();
assert_eq!(
params,
Params {
name: vec!["rustup".to_string()],
state: Some(State::Present),
..Default::default()
}
);
}

#[test]
fn test_parse_params_all_values() {
let yaml: Value = serde_yaml::from_str(
r#"
executable: yay
extra_args: "--nodeps --nodeps"
force: true
name:
- rustup
- bpftrace
state: latest
"#,
)
.unwrap();
let params: Params = parse_params(yaml).unwrap();
assert_eq!(
params,
Params {
executable: Some("yay".to_string()),
extra_args: Some("--nodeps --nodeps".to_string()),
force: Some(true),
name: vec!["rustup".to_string(), "bpftrace".to_string()],
state: Some(State::Latest),
}
);
}

#[test]
fn test_parse_params_random_field() {
let yaml: Value = serde_yaml::from_str(
r#"
name: rustup
foo: yea
"#,
)
.unwrap();
let error = parse_params::<Params>(yaml).unwrap_err();
assert_eq!(error.kind(), ErrorKind::InvalidData);
}
}
9 changes: 9 additions & 0 deletions rash_core/src/modules/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

pub fn default_false() -> Option<bool> {
Some(false)
}


pub fn default_true() -> Option<bool> {
Some(true)
}

0 comments on commit f0a342d

Please sign in to comment.