Skip to content

Commit

Permalink
Work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
gpoblon committed Oct 15, 2020
1 parent 5c012a3 commit 7256d81
Show file tree
Hide file tree
Showing 20 changed files with 390 additions and 179 deletions.
2 changes: 1 addition & 1 deletion rudder-lang/src/bin/rudderc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ fn main() {
let command = cli.as_command();
// Initialize logger and output
output.init(command, log_level, is_backtraced);
let ctx = cli.extract_parameters().unwrap_or_else(|e| {
let ctx = cli.extract_parameters().unwrap_or_else(|_e| {
// required before returning in order to have proper logging
output.print(
command,
Expand Down
13 changes: 8 additions & 5 deletions rudder-lang/src/generator/cfengine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ impl CFEngine {
}
EnumExpressionPart::Compare(var, e, item) => {
if let Some(true) = gc.enum_list.enum_is_global(*e) {
// FIXME: We need some translation here since not all enums are available in cfengine (ex debian_only)
item.fragment().to_string() // here
gc.enum_list.get_item_cfengine_name(*var, *item)
} else {
// concat var name + item
let prefix = &self.var_prefixes[var.fragment()];
Expand Down Expand Up @@ -180,10 +179,12 @@ impl CFEngine {
}
Statement::Fail(msg) => Ok(vec![Promise::usebundle(
"_abort",
None,
vec![quoted("policy_fail"), self.value_to_string(msg, true)?],
)]),
Statement::LogDebug(msg) => Ok(vec![Promise::usebundle(
"log_rudder_mode",
None,
vec![
quoted("log_debug"),
self.value_to_string(msg, true)?,
Expand All @@ -194,6 +195,7 @@ impl CFEngine {
)]),
Statement::LogInfo(msg) => Ok(vec![Promise::usebundle(
"log_rudder_mode",
None,
vec![
quoted("log_info"),
self.value_to_string(msg, true)?,
Expand All @@ -204,6 +206,7 @@ impl CFEngine {
)]),
Statement::LogWarn(msg) => Ok(vec![Promise::usebundle(
"log_rudder_mode",
None,
vec![
quoted("log_warn"),
self.value_to_string(msg, true)?,
Expand All @@ -219,11 +222,11 @@ impl CFEngine {
Some(c) => format!("!({})", c),
});
Ok(vec![if *outcome == Token::new("", "kept") {
Promise::usebundle("success", vec![])
Promise::usebundle("success", None, vec![])
} else if *outcome == Token::new("", "repaired") {
Promise::usebundle("repaired", vec![])
Promise::usebundle("repaired", None, vec![])
} else {
Promise::usebundle("error", vec![])
Promise::usebundle("error", None, vec![])
}])
}
Statement::Noop => Ok(vec![]),
Expand Down
61 changes: 42 additions & 19 deletions rudder-lang/src/generator/cfengine/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub fn quoted(s: &str) -> String {
format!("\"{}\"", s)
}

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum PromiseType {
Vars,
Methods,
Expand All @@ -43,7 +43,7 @@ impl fmt::Display for PromiseType {
}
}

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum AttributeType {
UseBundle,
Unless,
Expand Down Expand Up @@ -100,10 +100,12 @@ impl fmt::Display for AttributeType {
}
}

#[derive(Clone, PartialEq, Eq)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Promise {
/// Comments in output file
comments: Vec<String>,
/// Resource/state the promise calls
component: Option<String>,
/// Type of the promise
promise_type: PromiseType,
/// Target of the promise
Expand All @@ -120,9 +122,14 @@ const FALSE_CLASSES: [&str; 4] = ["false", "!any", "concat(\"!any\")", "concat(\

/// Promise constructors should allow avoiding common mistakes
impl Promise {
pub fn new<T: Into<String>>(promise_type: PromiseType, promiser: T) -> Self {
pub fn new<T: Into<String>>(
promise_type: PromiseType,
component: Option<String>,
promiser: T,
) -> Self {
Self {
promise_type,
component,
promiser: promiser.into(),
attributes: HashMap::new(),
comments: vec![],
Expand All @@ -145,12 +152,12 @@ impl Promise {

/// Shortcut for building a string variable with a raw value
pub fn string_raw<T: Into<String>, S: AsRef<str>>(name: T, value: S) -> Self {
Promise::new(PromiseType::Vars, name).attribute(AttributeType::String, value.as_ref())
Promise::new(PromiseType::Vars, None, name).attribute(AttributeType::String, value.as_ref())
}

/// Shortcut for building an slist variable with a list of values to be quoted
pub fn slist<T: Into<String>, S: AsRef<str>>(name: T, values: Vec<S>) -> Self {
Promise::new(PromiseType::Vars, name).attribute(
Promise::new(PromiseType::Vars, None, name).attribute(
AttributeType::Slist,
format!(
"{{{}}}",
Expand All @@ -164,8 +171,17 @@ impl Promise {
}

/// Shortcut for calling a bundle with parameters
pub fn usebundle<T: AsRef<str>>(bundle: T, parameters: Vec<String>) -> Self {
Promise::new(PromiseType::Methods, "method_call").attribute(
pub fn usebundle<T: AsRef<str>>(
bundle: T,
component: Option<&str>,
parameters: Vec<String>,
) -> Self {
Promise::new(
PromiseType::Methods,
component.map(|s| s.to_owned()),
"method_call",
)
.attribute(
AttributeType::UseBundle,
format!("{}({})", bundle.as_ref(), parameters.join(", ")),
)
Expand Down Expand Up @@ -218,7 +234,10 @@ impl Promise {
fn format(&self, index: usize, padding: usize) -> String {
let promiser = if self.promise_type == PromiseType::Methods {
// Methods need to be unique
format!("{}_{}", UNIQUE_ID, index)
match self.component.clone() {
Some(method) => format!("{}_{}_{}", method, UNIQUE_ID, index),
None => format!("{}_{}", UNIQUE_ID, index),
}
} else {
self.promiser.clone()
};
Expand Down Expand Up @@ -342,19 +361,23 @@ impl Method {
// Reporting context
let reporting_context = Promise::usebundle(
"_method_reporting_context",
Some(&self.report_component),
vec![
quoted(&self.report_component),
quoted(&self.report_parameter),
],
)
.comment(format!("{}:", self.report_component,))
.comment(format!("{}:", self.report_component))
.comment("")
.comment(format!(" {}", self.source,))
.comment(format!(" {}", self.source))
.comment("");

// Actual method call
let method =
Promise::usebundle(format!("{}_{}", self.resource, self.state), self.parameters);
let method = Promise::usebundle(
format!("{}_{}", self.resource, self.state),
Some(&self.report_component),
self.parameters,
);

if has_condition {
let na_condition = format!(
Expand All @@ -367,11 +390,11 @@ impl Method {
method.if_condition(self.condition.clone()),
// NA report
Promise::usebundle(
"_classes_noop",
"_classes_noop", Some(&self.report_component),
vec![na_condition.clone()],
)
.unless_condition(&self.condition),
Promise::usebundle("log_rudder", vec![
Promise::usebundle("log_rudder", Some(&self.report_component), vec![
quoted(&format!("Skipping method '{}' with key parameter '{}' since condition '{}' is not reached", &self.report_component, &self.report_parameter, self.condition)),
quoted(&self.report_parameter),
na_condition.clone(),
Expand Down Expand Up @@ -556,12 +579,12 @@ mod tests {
#[test]
fn format_promise() {
assert_eq!(
Promise::new(PromiseType::Vars, "test")
Promise::new(PromiseType::Vars, None, "test")
.format(0, LONGUEST_ATTRIBUTE_LEN + 3 + UNIQUE_ID_LEN),
"\"test\";"
);
assert_eq!(
Promise::new(PromiseType::Vars, "test")
Promise::new(PromiseType::Vars, None, "test")
.comment("test".to_string())
.format(0, LONGUEST_ATTRIBUTE_LEN + 3 + UNIQUE_ID_LEN),
" # test\n\"test\";"
Expand All @@ -587,7 +610,7 @@ mod tests {
.condition("debian".to_string())
.build()).to_string()
,
"bundle agent test {\n\n methods:\n # component:\n # \n # \n # \n \"${report_data.directive_id}_0\" usebundle => _method_reporting_context(\"component\", \"parameter\"),\n if => concat(\"debian\");\n \"${report_data.directive_id}_0\" usebundle => package_present(vim),\n if => concat(\"debian\");\n \"${report_data.directive_id}_0\" usebundle => _classes_noop(canonify(\"${class_prefix}_package_present_parameter\")),\n unless => concat(\"debian\");\n \"${report_data.directive_id}_0\" usebundle => log_rudder(\"Skipping method \'component\' with key parameter \'parameter\' since condition \'debian\' is not reached\", \"parameter\", canonify(\"${class_prefix}_package_present_parameter\"), canonify(\"${class_prefix}_package_present_parameter\"), @{args}),\n unless => concat(\"debian\");\n\n}"
"bundle agent test {\n\n methods:\n # component:\n # \n # \n # \n \"component_${report_data.directive_id}_0\" usebundle => _method_reporting_context(\"component\", \"parameter\"),\n if => concat(\"debian\");\n \"component_${report_data.directive_id}_0\" usebundle => package_present(vim),\n if => concat(\"debian\");\n \"component_${report_data.directive_id}_0\" usebundle => _classes_noop(canonify(\"${class_prefix}_package_present_parameter\")),\n unless => concat(\"debian\");\n \"component_${report_data.directive_id}_0\" usebundle => log_rudder(\"Skipping method \'component\' with key parameter \'parameter\' since condition \'debian\' is not reached\", \"parameter\", canonify(\"${class_prefix}_package_present_parameter\"), canonify(\"${class_prefix}_package_present_parameter\"), @{args}),\n unless => concat(\"debian\");\n\n}"
);
}

Expand All @@ -600,7 +623,7 @@ mod tests {
assert_eq!(
Bundle::agent("test")
.parameters(vec!["file".to_string(), "lines".to_string()])
.promise_group(vec![Promise::usebundle("test", vec![])])
.promise_group(vec![Promise::usebundle("test", None, vec![])])
.to_string(),
"bundle agent test(file, lines) {\n\n methods:\n \"${report_data.directive_id}_0\" usebundle => test();\n\n}"
);
Expand Down
3 changes: 1 addition & 2 deletions rudder-lang/src/generator/dsc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,7 @@ impl DSC {
}
EnumExpressionPart::Compare(var, e, item) => {
if let Some(true) = gc.enum_list.enum_is_global(*e) {
// We probably need some translation here since not all enums are available in cfengine (ex debian_only)
item.fragment().to_string() // here
gc.enum_list.get_item_cfengine_name(*var, *item)
} else {
// Temporary keep this piece of code in case it has a purpose
// // concat var name + item
Expand Down
14 changes: 13 additions & 1 deletion rudder-lang/src/ir/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ impl<'src> EnumList<'src> {
self.enums.get(&e).and_then(|e| e.item_metadata.get(&i))
}

/// Returns the item cfengine name (unchanged if no cfengine_name metadata)
pub fn get_item_cfengine_name(&self, var: Token<'src>, item: Token<'src>) -> String {
self.enum_item_metadata(var, item)
.and_then(|metadatas| metadatas.get("cfengine_name"))
.and_then(|cf_value| cf_value.as_str())
.unwrap_or(item.fragment())
.to_string()
}

/// Returns the item if it is global and None otherwise
pub fn global_enum(&self, e: Token<'src>) -> Option<&Token<'src>> {
self.global_items.get(&e)
Expand Down Expand Up @@ -177,7 +186,10 @@ impl<'src> EnumList<'src> {
// check for key name duplicate and insert reference
self.add_to_global(is_global, tree_name, &e.items)?;
// insert keys
self.enums.get_mut(&tree_name).expect("BUG: tree disapeared from 2 lines above").extend(e)?;
self.enums
.get_mut(&tree_name)
.expect("BUG: tree disapeared from 2 lines above")
.extend(e)?;
Ok(None)
}

Expand Down
3 changes: 3 additions & 0 deletions rudder-lang/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2019-2020 Normation SAS

// TODO remove silence compilation warnings. Tmp, helps working on testing loop (cleaner)
#![allow(warnings, unused)]

// `macro_use` attributes make related macro available to the current scope...
// but only for modules bring into scope after the statement
// so it needs to be put first
Expand Down
12 changes: 6 additions & 6 deletions rudder-lang/src/technique.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ use std::{
// Techniques are limited subsets of CFEngine in JSON representation
// that only carry method calls and Rudder metadata

// might change later
pub type TechniqueFmt = String;

// required Version type de/serializer
fn version_into_string<S>(v: &Version, s: S) -> std::result::Result<S::Ok, S::Error>
where
Expand Down Expand Up @@ -74,9 +77,6 @@ impl FromStr for Version {
}
}

// might change later
pub type TechniqueFmt = String;

/// Every Technique substructure has only 1 purpose: represent a Technique as json or rudderlang string
#[derive(Serialize, Deserialize)]
pub struct Technique {
Expand All @@ -91,7 +91,7 @@ impl Technique {

if is_technique_data {
let data = serde_json::from_str::<TechniqueData>(content)
.map_err(|e| Error::new(format!("Technique from JSON: {}", e)))?;
.map_err(|e| Error::new(format!("Technique from JSON technique: {}", e)))?;
Ok(Self {
r#type: "ncf_techniques".to_owned(),
version: 2,
Expand Down Expand Up @@ -155,7 +155,7 @@ impl TechniqueData {
.unzip();
let parameters_meta_fmt = match parameters_meta.is_empty() {
true => "".to_owned(),
false => format!("\n {}\n", parameters_meta.join(",\n ")),
false => format!("\n@ {}\n@", parameters_meta.join(",\n@ ")),
};

let calls = self
Expand Down Expand Up @@ -202,7 +202,7 @@ pub struct InterpolatedParameter {
impl InterpolatedParameter {
fn to_rudderlang(&self) -> Result<(String, String)> {
let parameter_meta = format!(
r#"{{ "name": "{}", "id": "{}", "description": "{}" }}"#,
r#"{{ "name" = "{}", "id" = "{}", "description" = "{}" }}"#,
self.name, self.id, self.description,
);
let parameter = self.name.replace("\"", "").replace(" ", "_").to_owned();
Expand Down
4 changes: 1 addition & 3 deletions rudder-lang/src/technique/from_ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ impl<'src> From<&IR2<'src>> for Technique {
bundle_name: extract_meta_string(meta, "name"),
description: extract_meta_string(meta, "description"),
name: extract_meta_string(meta, "name"),
// unwraping for now, TODO find better ways
version: Version::from_str(&extract_meta_string(meta, "version")).unwrap(),
category: extract_meta_string(meta, "category"),
interpolated_parameters,
Expand Down Expand Up @@ -118,8 +117,7 @@ fn format_expr(ir: &IR2, expr: &EnumExpressionPart) -> String {
}
EnumExpressionPart::Compare(var, tree, item) => {
if let Some(true) = ir.enum_list.enum_is_global(*var) {
// FIXME: We need some translation here since not all enums are available in cfengine (ex debian_only)
item.fragment().to_string() // here
ir.enum_list.get_item_cfengine_name(*var, *item)
} else {
// TODO ADD PREFIX ?
// concat var name + item
Expand Down
9 changes: 4 additions & 5 deletions rudder-lang/tests/techniques/parameters/technique.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
"type": "ncf_techniques",
"version": 1,
"data": {
"name": "parameters",
"bundle_name": "parameters",
"description": "technique using parameters",
"name": "parameters",
"version": "1.0",
"parameter": [
{
"name": "paramtest",
"id": "d74a03dd-5b0b-4b06-8dcf-b4e0cb387c60",
"description": "parameter test",
"constraints": {
"allow_whitespace_string": false,
"allow_empty_string": false,
Expand All @@ -17,10 +19,7 @@
"type": "string"
}
],
"bundle_name": "parameters",
"bundle_args": [
"paramtest"
],
"category": "ncf_techniques",
"method_calls": [
{
"class_context": "any",
Expand Down
Loading

0 comments on commit 7256d81

Please sign in to comment.