Skip to content

Commit

Permalink
Work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
gpoblon committed Mar 9, 2020
2 parents d25ff95 + f0bf4e3 commit 7391f88
Show file tree
Hide file tree
Showing 45 changed files with 874 additions and 93 deletions.
4 changes: 4 additions & 0 deletions rudder-lang/Cargo.toml
Expand Up @@ -8,6 +8,10 @@ homepage = "https://www.rudder.io"
repository = "https://github.com/Normation/rudder"
license = "GPL3"

[[bin]]
name = "rudderc"
path = "src/bin/main.rs"

[dependencies]
maplit = "^1.0"
serde = { version = "1.0", features = ["derive"] }
Expand Down
56 changes: 47 additions & 9 deletions rudder-lang/Makefile
@@ -1,20 +1,58 @@
.DEFAULT_GOAL := build
SHELL = /bin/bash
.DEFAULT_GOAL := build
SHELL := /bin/bash

DESTDIR := $(CURDIR)/target
REDHATOS := $(wildcard /etc/redhat-release*)
DEBIANOS := $(wildcard /etc/debian_version*)

ifneq ($(DEBIANOS),)
PKG_INSTALLER := apt
else ifneq ($(REDHATOS),)
PKG_INSTALLER := yum
endif

cargo:
curl https://sh.rustup.rs -sSf | sh -s -- -y
source $(HOME)/.cargo/env
export PATH=/root/.cargo/bin:$$PATH # temporary fix, won't be necessary

# should not be necessary either
deps:
apt update && apt upgrade -y
apt install -y perl
echo | cpan
perl -MCPAN -e 'install Config::IniFiles'
$(PKG_INSTALLER) update && $(PKG_INSTALLER) install -y perl python python3

build: deps
perl tools/generate-lib

build: deps
cargo build --release

postinstall:
$(DESTDIR)/opt/rudder/share/rudder-lang/tools/generate_lib

install:
mkdir -p $(DESTDIR)/opt/rudder/etc
mkdir -p $(DESTDIR)/opt/rudder/bin
mkdir -p $(DESTDIR)/opt/rudder/share/rudder-lang/lib
mkdir -p $(DESTDIR)/opt/rudder/share/rudder-lang/tools

# Install executable and helper scripts
install -m 755 target/release/rudderc $(DESTDIR)/opt/rudder/bin/rudderc
install -m 755 tools/tester.sh $(DESTDIR)/opt/rudder/share/rudder-lang/tools/tester.sh
install -m 755 tools/cfjson_tester $(DESTDIR)/opt/rudder/share/rudder-lang/tools/cfjson_tester
install -m 755 tools/generate_lib $(DESTDIR)/opt/rudder/share/rudder-lang/tools/generate_lib

# Copy configuration and library files
# required for rudderc execution
install -m 644 tools/rudderc.conf $(DESTDIR)/opt/rudder/etc/rudderc.conf
install -m 644 libs/corelib.rl $(DESTDIR)/opt/rudder/share/rudder-lang/lib/corelib.rl
install -m 644 libs/cfengine_core.rl $(DESTDIR)/opt/rudder/share/rudder-lang/lib/cfengine_core.rl
install -m 644 libs/oslib.rl $(DESTDIR)/opt/rudder/share/rudder-lang/lib/oslib.rl

# Not sure these should be copied on the server
# required by rudder lang at `cargo build` time
# install -m 644 tools/osbuilder.ron $(DESTDIR)/opt/rudder/share/rudder-lang/tools/osbuilder.ron
# generated by generate_lib
# install -m 644 libs/stdlib.rl $(DESTDIR)/opt/rudder/share/rudder-lang/lib/stdlib.rl
# install -m 644 tools/translate_config.toml $(DESTDIR)/opt/rudder/share/rudder-lang/tools/translate_config.toml


test:
cargo test

Expand Down
2 changes: 1 addition & 1 deletion rudder-lang/README.adoc
Expand Up @@ -60,6 +60,6 @@ The compiler is very pedantic to avoid defining invalid states as much as possib
== Tests

=== Required modules
- perl (script: *tools/generate-lib*)
- perl (script: *tools/generate_lib*)
=== Configuration
- *tools/rudderc.conf* to define the cfengine and ncf binaries paths, and the compilation, translation paths
30 changes: 15 additions & 15 deletions rudder-lang/libs/stdlib.rl
@@ -1,21 +1,21 @@
@format=0

resource variable(p0,p1)
resource environment(p0)
resource service(p0)
resource command(p0)
resource package(p0)
resource file(p0)
resource directory(p0)
resource http(p0)
resource service(p0)
resource environment(p0)
resource kernel_module(p0)
resource user(p0)
resource file(p0)
resource command(p0)
resource permissions(p0)
resource group(p0)
resource condition(p0)
resource http_request(p0,p1)
resource schedule(p0)
resource kernel_module(p0)
resource condition(p0)
resource package(p0)
resource sharedfile(p0,p1)
resource variable(p0,p1)
resource monitoring(p0)
resource sharedfile(p0)
resource group(p0)

command state execution(){}
command state execution_once(p1,p2,p3){}
Expand Down Expand Up @@ -101,8 +101,8 @@ file state symlink_present_option(p1,p2){}
file state template_expand(p1,p2,p3,p4){}
group state absent(){}
group state present(){}
http state request_check_status_headers(p1,p2,p3){}
http state request_content_headers(p1,p2,p3){}
http_request state check_status_headers(p2,p3){}
http_request state content_headers(p2,p3){}
kernel_module state configuration(p1){}
kernel_module state enabled_at_boot(){}
kernel_module state loaded(){}
Expand Down Expand Up @@ -158,8 +158,8 @@ service state started(){}
service state started_path(p1){}
service state stop(){}
service state stopped(){}
sharedfile state from_node(p1,p2){}
sharedfile state to_node(p1,p2,p3){}
sharedfile state from_node(p2){}
sharedfile state to_node(p2,p3){}
user state absent(){}
user state create(p1,p2,p3,p4,p5){}
user state fullname(p1){}
Expand Down
2 changes: 1 addition & 1 deletion rudder-lang/src/bin/main.rs
Expand Up @@ -18,7 +18,7 @@ use rudderc::{ compile::compile_file, file_paths, logger, translate::translate_f
///!
///! 2- json technique -> translate() -> rl
///!
///! 3- ncf library -> generate-lib() -> stdlib.rl + translate-config
///! 3- ncf library -> generate_lib() -> stdlib.rl + translate-config
///!

// MAIN
Expand Down
7 changes: 4 additions & 3 deletions rudder-lang/src/file_paths.rs
Expand Up @@ -61,6 +61,7 @@ pub fn get(exec_action: &str, default_paths: &PathBuf, opt_base: &Option<PathBuf
// Ease of read closure
let err_gen = |e: &str| Err(Error::User(format!("{}", e)));

println!("default paths: {:#?}", default_paths);
let config: toml::Value = match std::fs::read_to_string(default_paths) {
Err(e) => return err_gen(&format!("Could not read toml config file: {}", e)),
Ok(config_data) => match toml::from_str(&config_data) {
Expand All @@ -73,8 +74,8 @@ pub fn get(exec_action: &str, default_paths: &PathBuf, opt_base: &Option<PathBuf
Some(m) => m
};

let rudderclibs = match paths.get("rudderclibs") {
None => return err_gen("No rudderclibs section in toml config file"),
let rudderc_libs = match paths.get("rudderc_libs") {
None => return err_gen("No rudderc_libs section in toml config file"),
Some(m) => PathBuf::from(m.as_str().unwrap())
};
let translate_config = match paths.get("translate_config") {
Expand All @@ -83,7 +84,7 @@ pub fn get(exec_action: &str, default_paths: &PathBuf, opt_base: &Option<PathBuf
};

Ok((
rudderclibs,
rudderc_libs,
translate_config,
get_input(exec_action, paths, opt_base, opt_input)?,
get_output(exec_action, paths, opt_base, opt_input, opt_output)?,
Expand Down
31 changes: 22 additions & 9 deletions rudder-lang/src/generators/cfengine.rs
Expand Up @@ -84,18 +84,30 @@ impl CFEngine {
}
fn format_case_expr(&mut self, gc: &AST, case: &EnumExpression) -> Result<String> {
Ok(match case {
EnumExpression::And(e1, e2) => format!(
"({}).({})",
self.format_case_expr(gc, e1)?,
self.format_case_expr(gc, e2)?
),
EnumExpression::And(e1, e2) => {
let mut lexpr = self.format_case_expr(gc, e1)?;
let mut rexpr = self.format_case_expr(gc, e2)?;
if lexpr.contains("|") {
lexpr = format!("({})", lexpr);
}
if rexpr.contains("|") {
rexpr = format!("({})", rexpr);
}
format!("{}.{}", lexpr, rexpr)
},
EnumExpression::Or(e1, e2) => format!(
"({})|({})",
"{}|{}",
self.format_case_expr(gc, e1)?,
self.format_case_expr(gc, e2)?
),
// TODO what about classes that have not yet been set ? can it happen ?
EnumExpression::Not(e1) => format!("!({})", self.format_case_expr(gc, e1)?),
EnumExpression::Not(e1) => {
let mut expr = self.format_case_expr(gc, e1)?;
if expr.contains("|") || expr.contains("&") {
expr = format!("!({})", expr);
}
format!("!{}", expr)
},
EnumExpression::Compare(var, e, item) => {
if gc.enum_list.is_global(*e) {
let final_enum = gc.enum_list.find_descendant_enum(*e, *item);
Expand Down Expand Up @@ -168,8 +180,9 @@ impl CFEngine {
", ",
)?;
let class = self.format_class(in_class)?;
let state_param = if sd.state_params.len() > 0 {
if let Ok(param) = self.parameter_to_cfengine(&sd.state_params[0]) {
println!("state param empty ? {:#?}", sd.state_params);
let state_param = if sd.resource_params.len() > 0 {
if let Ok(param) = self.parameter_to_cfengine(&sd.resource_params[0]) {
format!(", {}", param)
} else {
"".to_string()
Expand Down
21 changes: 13 additions & 8 deletions rudder-lang/src/translate.rs
Expand Up @@ -360,7 +360,11 @@ fn translate_condition(_config: &toml::Value, cond: &str) -> Result<String> {
// matches a word that can be negative and that be followed by .| + word... possibly wrapped into parenthesis
static ref OS_RE: Regex = Regex::new(
// OS part: debian_9_0 \ And optional condition: (!(ubuntu|otheros).something)
r"^\(?(?P<os>([a-zA-Z\d]+)(_([a-zA-Z\d]+))*(\|([a-zA-Z\d]+)(_([a-zA-Z\d]+))*)*)\)?(.(?P<cdt>\(\(*!*\(*\w+\)*([.|]\(*!*\(*\w+\)*)*\)))?$"
// r"^\(?(?P<os>([a-zA-Z\d]+)(_([a-zA-Z\d]+))*(\|([a-zA-Z\d]+)(_([a-zA-Z\d]+))*)*)\)?(.(?P<cdt>\(\(*!*\(*\w+\)*([.|]\(*!*\(*\w+\)*)*\)))?$"
r"^(\(*\w+\(*\)*(\||\.)\(*\w+\)*)*$"
).unwrap();
static ref AND_RE: Regex = Regex::new(
r"^[^\.]+((?P<and>\.)[^\.]+)*$"
).unwrap();
}

Expand Down Expand Up @@ -390,15 +394,16 @@ fn translate_condition(_config: &toml::Value, cond: &str) -> Result<String> {
cond
)))
}
let os = OS_RE.replace_all(cond, "$os");
let mut result = os.to_string();
let cdt = OS_RE.replace_all(cond, "$cdt");
if cdt.len() > 0 {
result.push_str(&format!("&& {}", cdt));
};
// let os = OS_RE.replace_all(cond, "$os");
// let mut result = os.to_string();
// let cdt = OS_RE.replace_all(cond, "$cdt");
// if cdt.len() > 0 {
// result.push_str(&format!(" & {}", cdt));
// };
let transformed_and = cond.replace(".", "&");
// TODO here we consider any match is an os match, should we have an OS whitelist ?
// OS are global enum so we don't have to say which enum to match
return Ok(result.into());
return Ok(transformed_and.into());
}

// TODO detect condition expressions
Expand Down
15 changes: 15 additions & 0 deletions rudder-lang/tests/test_files/tester/condition_andor/technique.cf
@@ -0,0 +1,15 @@
# @name condition andor
# @description Kernel simplest
# @version 1.0


bundle agent condition_andor
{
vars:
"resources_dir" string => "${this.promise_dirname}/resources";
methods:
"Kernel module loaded_${report_data.directive_id}_0" usebundle => _method_reporting_context("Kernel module loaded", "test"),
if => concat("(debian.linux)|ubuntu.windows|(linux.ubuntu)");
"Kernel module loaded_${report_data.directive_id}_0" usebundle => kernel_module_loaded("test"),
if => concat("(debian.linux)|ubuntu.windows|(linux.ubuntu)");
}
18 changes: 18 additions & 0 deletions rudder-lang/tests/test_files/tester/condition_andor/technique.json
@@ -0,0 +1,18 @@
{
"name": "condition andor",
"description": "Kernel simplest",
"version": "1.0",
"bundle_name": "condition_andor",
"bundle_args": [],
"parameter": [],
"method_calls": [
{
"class_context": "(debian|linux)|ubuntu.windows|(linux.ubuntu)",
"component": "Kernel module loaded",
"method_name": "kernel_module_loaded",
"args": [
"test"
]
}
]
}
14 changes: 14 additions & 0 deletions rudder-lang/tests/test_files/tester/condition_andor/technique.rl
@@ -0,0 +1,14 @@
# This file has been generated with rltranslate
@format=0

@name="condition andor"
@description="Kernel simplest"
@version="1.0"
@parameters=[]

resource condition_andor()

condition_andor state technique() {
@component = "Kernel module loaded"
if (debian|linux)|ubuntu&windows|(linux&ubuntu) => kernel_module("test").loaded() as kernel_module_loaded_test
}
@@ -0,0 +1,15 @@
# generated by rudder-lang
# @name condition andor
# @description Kernel simplest
# @version 1.0

bundle agent condition_andor_technique
{
vars:
"resources_dir" string => "${this.promise_dirname}/resources";
methods:
"Kernel module loaded_${report_data.directive_id}_0" usebundle => _method_reporting_context("Kernel module loaded", "test"),
if => concat("debian|linux|ubuntu.windows|linux.ubuntu");
"Kernel module loaded_${report_data.directive_id}_0" usebundle => kernel_module_loaded("test"),
if => concat("debian|linux|ubuntu.windows|linux.ubuntu");
}
@@ -0,0 +1,18 @@
{
"name": "condition andor",
"description": "Kernel simplest",
"version": "1.0",
"bundle_name": "condition_andor_technique",
"bundle_args": [],
"parameter": [],
"method_calls": [
{
"class_context": "debian|linux|ubuntu.windows|linux.ubuntu",
"component": "Kernel module loaded",
"method_name": "kernel_module_loaded",
"args": [
"test"
]
}
]
}
@@ -0,0 +1,18 @@
{
"name": "condition any bsd",
"description": "Kernel simplest",
"version": "1.0",
"bundle_name": "condition_anybsd",
"bundle_args": [],
"parameter": [],
"method_calls": [
{
"class_context": "(dragonfly|freebsd|netbsd|openbsd)",
"component": "Kernel module loaded",
"method_name": "kernel_module_loaded",
"args": [
"test"
]
}
]
}
14 changes: 14 additions & 0 deletions rudder-lang/tests/test_files/tester/condition_anybsd/technique.rl
@@ -0,0 +1,14 @@
# This file has been generated with rltranslate
@format=0

@name="condition any bsd"
@description="Kernel simplest"
@version="1.0"
@parameters=[]

resource condition_anybsd()

condition_anybsd state technique() {
@component = "Kernel module loaded"
if dragonfly|freebsd|netbsd|openbsd => kernel_module("test").loaded() as kernel_module_loaded_test
}

0 comments on commit 7391f88

Please sign in to comment.