Skip to content

Commit

Permalink
v0.2.12: more rhai features
Browse files Browse the repository at this point in the history
- More features for rhai scripts
  * json/yaml/base64 encode/decode
  * http client
- Optional conditions for the operator
- Conditions results are passed as env to the agent
- Re-install on condition change
  • Loading branch information
sebt3 committed May 30, 2024
1 parent 7c4f35a commit d9e2a39
Show file tree
Hide file tree
Showing 14 changed files with 502 additions and 38 deletions.
257 changes: 244 additions & 13 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "vynil"
version = "0.2.11"
version = "0.2.12"
authors = ["Sébastien Huss <sebastien.huss@gmail.com>"]
edition = "2021"
license = " BSD-3-Clause"
Expand Down
2 changes: 1 addition & 1 deletion agent/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "agent"
version = "0.2.11"
version = "0.2.12"
authors = ["Sébastien Huss <sebastien.huss@gmail.com>"]
edition = "2021"
license = " BSD-3-Clause"
Expand Down
2 changes: 1 addition & 1 deletion agent/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ RUN touch package/src/lib.rs k8s/src/lib.rs agent/src/lib.rs agent/src/main.rs \
# Then create the intermediary image with run-time dependencies installed
FROM debian:${DEBIAN_VERSION}-slim as middle
ARG HELM_VERSION=v3.10.3
ARG KUBECTL_VERSION=v1.25.7
ARG KUBECTL_VERSION=v1.29.3
ARG TF_VERSION=1.3.6
ARG DEB_PACKAGES="git jq curl tar gzip unzip openssl openssh-client ca-certificates postgresql-client"
# hadolint ignore=DL3008,DL4006,SC2035
Expand Down
2 changes: 1 addition & 1 deletion dist/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "dist"
version = "0.2.11"
version = "0.2.12"
authors = ["Sébastien Huss <sebastien.huss@gmail.com>"]
edition = "2021"
license = " BSD-3-Clause"
Expand Down
4 changes: 2 additions & 2 deletions dist/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ARG RUST_VERSION=1.77.1
ARG DEBIAN_VERSION=bookworm
FROM rust:${RUST_VERSION}-slim-${DEBIAN_VERSION} as builder
ARG BUILD_DEPS="git"
ARG BUILD_DEPS="git pkg-config libssl-dev"
WORKDIR /usr/src/dist
COPY Cargo.lock .
COPY dist/parent.toml ./Cargo.toml
Expand Down Expand Up @@ -29,7 +29,7 @@ RUN touch package/src/lib.rs k8s/src/lib.rs dist/src/main.rs \
# Finally create the target image with run-time dependencies installed
FROM debian:${DEBIAN_VERSION}-slim as target
ARG HELM_VERSION=v3.10.3
ARG KUBECTL_VERSION=v1.25.7
ARG KUBECTL_VERSION=v1.29.3
ARG TF_VERSION=1.3.6
ARG DEB_PACKAGES="git jq curl tar gzip unzip openssl openssh-client ca-certificates"
# hadolint ignore=DL3027,DL3008,DL4006,SC2035
Expand Down
2 changes: 1 addition & 1 deletion k8s/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "k8s"
version = "0.2.11"
version = "0.2.12"
edition = "2021"
license = " BSD-3-Clause"

Expand Down
2 changes: 1 addition & 1 deletion operator/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "operator"
version = "0.2.11"
version = "0.2.12"
authors = ["Sébastien Huss <sebastien.huss@gmail.com>"]
edition = "2021"
default-run = "operator"
Expand Down
4 changes: 3 additions & 1 deletion package/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "package"
version = "0.2.11"
version = "0.2.12"
authors = ["Sébastien Huss <sebastien.huss@gmail.com>"]
edition = "2021"
license = " BSD-3-Clause"
Expand All @@ -20,6 +20,8 @@ handlebars = "4.3.6"
indent = "0.1.1"
schemars = { version = "0.8.11", features = ["chrono"] }
tokio = { version = "1.23.0", features = ["macros", "rt-multi-thread"] }
base64 = "0.22.1"
reqwest = "0.12.4"

[lib]
name = "package"
Expand Down
203 changes: 203 additions & 0 deletions package/src/http_script.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
use rhai::{Engine,Map,Dynamic,ImmutableString};
use base64::{engine::general_purpose::STANDARD, Engine as _};
use reqwest::{Client,Response};

use serde::{Deserialize, Serialize};
use tokio::runtime::Handle;

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Head {
pub get: Map
}
impl Head {
pub fn new() -> Head {
Head {
get: Map::new()
}
}
pub fn from(src: Map) -> Head {
Head {
get: src
}
}
pub fn bearer(token: &str) -> Head {
let mut this = Head::new();
this.add_bearer(token);
this
}
pub fn basic(username: &str, password: &str) -> Head {
let mut this = Head::new();
this.add_basic(username, password);
this
}
pub fn add_bearer(&mut self, token: &str) -> &mut Head {
self.get.insert("Authorization".to_string().into(), format!("Bearer {token}").into());
self
}
pub fn add_basic(&mut self, username: &str, password: &str) -> &mut Head {
let hash = STANDARD.encode(format!("{username}:{password}"));
self.get.insert("Authorization".to_string().into(), format!("Basic {hash}").into());
self
}
pub fn add_json_content(&mut self) -> &mut Head {
self.get.insert("Content-Type".to_string().into(), "application/json; charset=utf-8".to_string().into());
self
}
pub fn add_json_accept(&mut self) -> &mut Head {
self.get.insert("Accept".to_string().into(), "application/json".to_string().into());
self
}
pub fn add_json(&mut self) -> &mut Head {
self.add_json_content().add_json_accept()
}
}

fn http_get(url: &str, headers: Map) -> Response {
let mut client = Client::new().get(url.to_string());
for (key,val) in headers {
client = client.header(key.to_string(), val.to_string());
}
tokio::task::block_in_place(|| {Handle::current().block_on(async move {
client.send().await.unwrap()
})})
}
fn http_patch(url: &str, headers: Map, body: &str) -> Response {
let mut client = Client::new().patch(url.to_string()).body(body.to_string());
for (key,val) in headers {
client = client.header(key.to_string(), val.to_string());
}
tokio::task::block_in_place(|| {Handle::current().block_on(async move {
client.send().await.unwrap()
})})
}
fn http_post(url: &str, headers: Map, body: &str) -> Response {
let mut client = Client::new().post(url.to_string()).body(body.to_string());
for (key,val) in headers {
client = client.header(key.to_string(), val.to_string());
}
tokio::task::block_in_place(|| {Handle::current().block_on(async move {
client.send().await.unwrap()
})})
}
fn http_put(url: &str, headers: Map, body: &str) -> Response {
let mut client = Client::new().put(url.to_string()).body(body.to_string());
for (key,val) in headers {
client = client.header(key.to_string(), val.to_string());
}
tokio::task::block_in_place(|| {Handle::current().block_on(async move {
client.send().await.unwrap()
})})
}
fn http_check(url: &str, headers: Map, code: i64) -> bool {
let res = http_get(url, headers);
i64::from(res.status().as_u16())==code
}
pub fn add_http_to_engine(e: &mut Engine) {
// TODO: http_get[,_json](uri,headers)
// TODO: http_[patch|post|put][,_json](uri,headers,payload)
e.register_fn("http_check", move |url:ImmutableString,headers:Map,code:i64| -> bool {
http_check(&url.to_string(),headers,i64::from(code))
});
e.register_fn("http_get", move |url:ImmutableString,headers:Map| -> Dynamic {
let res = http_get(&url.to_string(),headers);
let mut ret = Map::new();
ret.insert("code".to_string().into(), Dynamic::from(res.status().as_u16()));
tokio::task::block_in_place(|| {Handle::current().block_on(async move {
ret.insert("body".to_string().into(), Dynamic::from(res.text().await.unwrap()));
ret.into()
})})
});
e.register_fn("http_post", move |url:ImmutableString,headers:Map,data:ImmutableString| -> Dynamic {
let res = http_post(&url.to_string(),headers,&data.to_string());
let mut ret = Map::new();
ret.insert("code".to_string().into(), Dynamic::from(res.status().as_u16()));
tokio::task::block_in_place(|| {Handle::current().block_on(async move {
ret.insert("body".to_string().into(), Dynamic::from(res.text().await.unwrap()));
ret.into()
})})
});
e.register_fn("http_patch", move |url:ImmutableString,headers:Map,data:ImmutableString| -> Dynamic {
let res = http_patch(&url.to_string(),headers,&data.to_string());
let mut ret = Map::new();
ret.insert("code".to_string().into(), Dynamic::from(res.status().as_u16()));
tokio::task::block_in_place(|| {Handle::current().block_on(async move {
ret.insert("body".to_string().into(), Dynamic::from(res.text().await.unwrap()));
ret.into()
})})
});
e.register_fn("http_put", move |url:ImmutableString,headers:Map,data:ImmutableString| -> Dynamic {
let res = http_put(&url.to_string(),headers,&data.to_string());
let mut ret = Map::new();
ret.insert("code".to_string().into(), Dynamic::from(res.status().as_u16()));
tokio::task::block_in_place(|| {Handle::current().block_on(async move {
ret.insert("body".to_string().into(), Dynamic::from(res.text().await.unwrap()));
ret.into()
})})
});
e.register_fn("http_get_json", move |url:ImmutableString,headers:Map| -> Dynamic {
let mut h = Head::from(headers);h.add_json_accept();
let res = http_get(&url.to_string(),h.get);
let mut ret = Map::new();
ret.insert("code".to_string().into(), Dynamic::from(res.status().as_u16()));
tokio::task::block_in_place(|| {Handle::current().block_on(async move {
let text = res.text().await.unwrap();
ret.insert("json".to_string().into(), serde_json::from_str(&text).unwrap());
ret.insert("body".to_string().into(), Dynamic::from(text));
ret.into()
})})
});
e.register_fn("http_post_json", move |url:ImmutableString,headers:Map,data:Dynamic| -> Dynamic {
let mut h = Head::from(headers);h.add_json();
let res = http_post(&url.to_string(),h.get,&serde_json::to_string(&data).unwrap());
let mut ret = Map::new();
ret.insert("code".to_string().into(), Dynamic::from(res.status().as_u16()));
tokio::task::block_in_place(|| {Handle::current().block_on(async move {
let text = res.text().await.unwrap();
ret.insert("json".to_string().into(), serde_json::from_str(&text).unwrap());
ret.insert("body".to_string().into(), Dynamic::from(text));
ret.into()
})})
});
e.register_fn("http_patch_json", move |url:ImmutableString,headers:Map,data:Dynamic| -> Dynamic {
let mut h = Head::from(headers);h.add_json();
let res = http_patch(&url.to_string(),h.get,&serde_json::to_string(&data).unwrap());
let mut ret = Map::new();
ret.insert("code".to_string().into(), Dynamic::from(res.status().as_u16()));
tokio::task::block_in_place(|| {Handle::current().block_on(async move {
let text = res.text().await.unwrap();
ret.insert("json".to_string().into(), serde_json::from_str(&text).unwrap());
ret.insert("body".to_string().into(), Dynamic::from(text));
ret.into()
})})
});
e.register_fn("http_put_json", move |url:ImmutableString,headers:Map,data:Dynamic| -> Dynamic {
let mut h = Head::from(headers);h.add_json();
let res = http_put(&url.to_string(),h.get,&serde_json::to_string(&data).unwrap());
let mut ret = Map::new();
ret.insert("code".to_string().into(), Dynamic::from(res.status().as_u16()));
tokio::task::block_in_place(|| {Handle::current().block_on(async move {
let text = res.text().await.unwrap();
ret.insert("json".to_string().into(), serde_json::from_str(&text).unwrap());
ret.insert("body".to_string().into(), Dynamic::from(text));
ret.into()
})})
});
e.register_fn("http_header", || -> Map {
Head::new().get
});
e.register_fn("http_header_basic", |user:ImmutableString,pass:ImmutableString| -> Map {
Head::basic(&user.to_string(),&pass.to_string()).get
});
e.register_fn("http_header_bearer", |token:ImmutableString| -> Map {
Head::bearer(&token.to_string()).get
});
/*e.register_fn("http_header_json", || -> Map {
let mut r = Head::new();r.add_json();r.get
});
e.register_fn("http_header_json_basic", |user:ImmutableString,pass:ImmutableString| -> Map {
let mut r = Head::basic(&user.to_string(),&pass.to_string());r.add_json();r.get
});
e.register_fn("http_header_json_bearer", |token:ImmutableString| -> Map {
let mut r = Head::bearer(&token.to_string());r.add_json();r.get
});*/
}
1 change: 1 addition & 0 deletions package/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod k8s_script;
mod pkg_script;
mod http_script;
pub mod shell;
pub mod script;
pub mod terraform;
Expand Down
36 changes: 33 additions & 3 deletions package/src/pkg_script.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use std::{fs, path::{Path,PathBuf}};
use anyhow::{Result, bail};
use base64::{engine::general_purpose::STANDARD, Engine as _};
use crate::shell;
use rhai::{Engine, Dynamic, ImmutableString};
use rhai::{Engine, Dynamic, ImmutableString,Map};
use crate::terraform::save_to_tf;
use std::collections::HashMap;
use std::{collections::HashMap,env};
use serde_json;
use serde_yaml;

fn explode_to_tf(src: &str, dest: &str, base: &str) -> Result<()> {
let content = fs::read_to_string(src)
Expand Down Expand Up @@ -76,4 +79,31 @@ pub fn add_pkg_to_engine(e: &mut Engine) {
tracing::error!("Failed to explode {source} to {dest}/{base}: {e:}");
}};
});
}
e.register_fn("get_env", |var: ImmutableString| -> ImmutableString {
match env::var(var.to_string()) {
Ok(s) => s.into(),
Err(_e) => "".into()
}
});
e.register_fn("base64_decode", |val: ImmutableString| -> ImmutableString {
String::from_utf8(STANDARD.decode(val.to_string()).unwrap()).unwrap().into()
});
e.register_fn("base64_encode", |val: ImmutableString| -> ImmutableString {
STANDARD.encode(val.to_string()).into()
});
e.register_fn("json_encode", |val: Dynamic| -> ImmutableString {
serde_json::to_string(&val).unwrap().into()
});
e.register_fn("json_encode", |val: Map| -> ImmutableString {
serde_json::to_string(&val).unwrap().into()
});
e.register_fn("json_decode", |val: ImmutableString| -> Dynamic {
serde_json::from_str(&val.to_string()).unwrap()
});
e.register_fn("yaml_encode", |val: &Dynamic| -> ImmutableString {
serde_yaml::to_string(val).unwrap().into()
});
e.register_fn("yaml_decode", |val: ImmutableString| -> Dynamic {
serde_yaml::from_str(&val.to_string()).unwrap()
});
}
4 changes: 2 additions & 2 deletions package/src/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::{process, path::{PathBuf, Path}};
use anyhow::{Result, bail, anyhow};
use crate::pkg_script::add_pkg_to_engine;
use crate::k8s_script::add_k8s_to_engine;
use crate::http_script::add_http_to_engine;
use k8s::{Client, get_client};

pub fn new_base_context(category:String, component:String, instance:String, config:&serde_json::Map<String, serde_json::Value>) -> Scope<'static> {
Expand Down Expand Up @@ -49,10 +50,9 @@ fn create_engine(client: &Client) -> Engine {
e.register_fn("log_warn", |s:ImmutableString| tracing::warn!("{s}"));
e.register_fn("log_error", |s:ImmutableString| tracing::error!("{s}"));
add_pkg_to_engine(&mut e);
add_http_to_engine(&mut e);
add_k8s_to_engine(&mut e,client);
add_to_engine(&mut e, "fn assert(cond, mess) {if (!cond){throw mess}}", Scope::new());
// TODO: Add an http client (download/get/post/put)
// TODO: Add a kubectl wrapper
e
}

Expand Down
Loading

0 comments on commit d9e2a39

Please sign in to comment.