From d4a59e3b1c3dd352e04130ed0cd688a3363e75fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Huss?= Date: Tue, 7 May 2024 15:34:04 +0200 Subject: [PATCH] Fix 0.2.9 --- Cargo.lock | 6 +- deploy/crd/crd.yaml | 4 + k8s/Cargo.toml | 2 +- k8s/src/distrib.rs | 1 + k8s/src/handlers.rs | 45 +++++++--- k8s/src/install.rs | 4 + k8s/src/yaml.rs | 6 +- operator/Cargo.toml | 1 + operator/src/distrib.rs | 3 +- operator/src/events.rs | 10 +++ operator/src/install.rs | 63 +++++++++----- operator/src/lib.rs | 2 +- package/Cargo.toml | 5 +- package/src/lib.rs | 3 +- package/src/script.rs | 176 +++++++++++++++++++++++++++------------ package/src/shell.rs | 8 +- package/src/terraform.rs | 4 +- 17 files changed, 242 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cb3afba..812cf63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1426,13 +1426,13 @@ dependencies = [ "chrono", "k8s-openapi", "kube", - "log", "openapiv3", "schemars", "serde", "serde_json", "serde_yaml", "sha256", + "tracing", ] [[package]] @@ -1830,6 +1830,7 @@ dependencies = [ "opentelemetry-otlp", "package", "prometheus", + "rhai", "schemars", "serde", "serde_json", @@ -1866,12 +1867,13 @@ dependencies = [ "handlebars", "indexmap 1.9.3", "k8s", - "log", "regex", "rhai", "schemars", "serde", "serde_json", + "tokio", + "tracing", ] [[package]] diff --git a/deploy/crd/crd.yaml b/deploy/crd/crd.yaml index bb8f232..1160264 100644 --- a/deploy/crd/crd.yaml +++ b/deploy/crd/crd.yaml @@ -19,6 +19,10 @@ spec: jsonPath: .spec.url name: url type: string + - description: Git branch + jsonPath: .spec.branch + name: branch + type: string - description: Update schedule jsonPath: .spec.schedule name: schedule diff --git a/k8s/Cargo.toml b/k8s/Cargo.toml index 1d53ae6..96a022c 100644 --- a/k8s/Cargo.toml +++ b/k8s/Cargo.toml @@ -16,7 +16,7 @@ k8s-openapi = { version = "0.21.0", features = ["latest"], default-features = fa chrono = "0.4.24" anyhow = "1.0.70" sha256 = "1.1.2" -log = "0.4.17" +tracing = "0.1.37" [dependencies.kube] features = ["runtime", "client", "derive"] diff --git a/k8s/src/distrib.rs b/k8s/src/distrib.rs index e10caa1..374931e 100644 --- a/k8s/src/distrib.rs +++ b/k8s/src/distrib.rs @@ -61,6 +61,7 @@ impl DistribComponent { #[kube(kind = "Distrib", group = "vynil.solidite.fr", version = "v1")] #[kube(status = "DistribStatus", shortname = "dist", printcolumn = r#" {"name":"url", "type":"string", "description":"Git url", "jsonPath":".spec.url"}, + {"name":"branch", "type":"string", "description":"Git branch", "jsonPath":".spec.branch"}, {"name":"schedule", "type":"string", "description":"Update schedule", "jsonPath":".spec.schedule"}, {"name":"last_updated", "type":"string", "description":"Last update date", "format": "date-time", "jsonPath":".status.last_updated"}"#)] pub struct DistribSpec { diff --git a/k8s/src/handlers.rs b/k8s/src/handlers.rs index e83efdf..b77ec43 100644 --- a/k8s/src/handlers.rs +++ b/k8s/src/handlers.rs @@ -9,9 +9,9 @@ pub struct InstallHandler { api: Api, } impl InstallHandler { - #[must_use] pub fn new(cl: Client, ns: &str) -> InstallHandler { + #[must_use] pub fn new(cl: &Client, ns: &str) -> InstallHandler { InstallHandler { - api: Api::namespaced(cl, ns), + api: Api::namespaced(cl.clone(), ns), } } pub async fn have(&mut self, name: &str) -> bool { @@ -33,9 +33,9 @@ pub struct DistribHandler { api: Api, } impl DistribHandler { - #[must_use] pub fn new(cl: Client) -> DistribHandler { + #[must_use] pub fn new(cl: &Client) -> DistribHandler { DistribHandler { - api: Api::all(cl), + api: Api::all(cl.clone()), } } pub async fn have(&mut self, name: &str) -> bool { @@ -53,14 +53,14 @@ impl DistribHandler { } } -use k8s_openapi::api::networking::v1::Ingress; +pub use k8s_openapi::api::networking::v1::Ingress; pub struct IngressHandler { api: Api, } impl IngressHandler { - #[must_use] pub fn new(cl: Client, ns: &str) -> IngressHandler { + #[must_use] pub fn new(cl: &Client, ns: &str) -> IngressHandler { IngressHandler { - api: Api::namespaced(cl, ns), + api: Api::namespaced(cl.clone(), ns), } } pub async fn have(&mut self, name: &str) -> bool { @@ -78,14 +78,14 @@ impl IngressHandler { } } -use k8s_openapi::api::core::v1::Secret; +pub use k8s_openapi::api::core::v1::Secret; pub struct SecretHandler { api: Api, } impl SecretHandler { - #[must_use] pub fn new(cl: Client, ns: &str) -> SecretHandler { + #[must_use] pub fn new(cl: &Client, ns: &str) -> SecretHandler { SecretHandler { - api: Api::namespaced(cl, ns), + api: Api::namespaced(cl.clone(), ns), } } pub async fn have(&mut self, name: &str) -> bool { @@ -102,3 +102,28 @@ impl SecretHandler { self.api.get(name).await } } + +pub use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition; +pub struct CustomResourceDefinitionHandler { + api: Api, +} +impl CustomResourceDefinitionHandler { + #[must_use] pub fn new(cl: &Client) -> CustomResourceDefinitionHandler { + CustomResourceDefinitionHandler { + api: Api::all(cl.clone()), + } + } + pub async fn have(&mut self, name: &str) -> bool { + let lp = ListParams::default(); + let list = self.api.list(&lp).await.unwrap(); + for secret in list { + if secret.metadata.name.clone().unwrap() == name { + return true; + } + } + false + } + pub async fn get(&mut self, name: &str) -> Result { + self.api.get(name).await + } +} diff --git a/k8s/src/install.rs b/k8s/src/install.rs index 39e7040..2074a6e 100644 --- a/k8s/src/install.rs +++ b/k8s/src/install.rs @@ -16,6 +16,7 @@ pub const STATUS_DESTROYED: &str = "destroyed"; pub const STATUS_AGENT_STARTED: &str = "agent started"; pub const STATUS_MISSING_DIST: &str = "missing distribution"; pub const STATUS_MISSING_COMP: &str = "missing component"; +pub const STATUS_CHECK_FAIL: &str = "Validations failed"; pub const STATUS_MISSING_PROV: &str = "missing provider config"; pub const STATUS_MISSING_DEPS: &str = "missing dependencies"; pub const STATUS_WAITING_DEPS: &str = "waiting dependencies"; @@ -181,6 +182,9 @@ impl Install { pub async fn update_status_missing_component(&self, client: Client, manager: &str, errors: Vec) -> Result { self.update_status_typed(client, manager, errors, STATUS_MISSING_COMP).await } + pub async fn update_status_check_failed(&self, client: Client, manager: &str, errors: Vec) -> Result { + self.update_status_typed(client, manager, errors, STATUS_CHECK_FAIL).await + } pub async fn update_status_missing_provider(&self, client: Client, manager: &str, errors: Vec) -> Result { self.update_status_typed(client, manager, errors, STATUS_MISSING_PROV).await } diff --git a/k8s/src/yaml.rs b/k8s/src/yaml.rs index a72c30e..fb18c43 100644 --- a/k8s/src/yaml.rs +++ b/k8s/src/yaml.rs @@ -274,15 +274,15 @@ impl Component { } } if skip { - log::warn!("Skipping option \"{}\" while updating type structure from default values", key); - log::info!("you should set \"type: array\" and a correct \"items\" definition for option \"{}\" so later validation will work", key); + tracing::warn!("Skipping option \"{}\" while updating type structure from default values", key); + tracing::info!("you should set \"type: array\" and a correct \"items\" definition for option \"{}\" so later validation will work", key); } else if let Some(opts) = schema.schema_data.default.as_ref() { // That option have a default value, update its properties let final_schema = &schema_for_value!(opts).schema; let objdef = serde_json::from_str(serde_json::to_string(final_schema)?.as_str())?; merge_json( &mut val, objdef); add_defaults(&mut val); - log::debug!("{key} after default : {:}", serde_yaml::to_string(&val).unwrap()); + tracing::debug!("{key} after default : {:}", serde_yaml::to_string(&val).unwrap()); *self.options.get_mut(key.as_str()).unwrap() = val; } } diff --git a/operator/Cargo.toml b/operator/Cargo.toml index 6195bac..2eb8280 100644 --- a/operator/Cargo.toml +++ b/operator/Cargo.toml @@ -49,6 +49,7 @@ async-trait = "0.1.68" either = "1.8.1" base64 = "0.21.2" json-patch = {version = "=1.2.0"} +rhai = { version = "1.12.0", features = ["sync", "serde"] } [dependencies.kube] features = ["runtime", "client", "derive"] diff --git a/operator/src/distrib.rs b/operator/src/distrib.rs index 37a1647..865b7d7 100644 --- a/operator/src/distrib.rs +++ b/operator/src/distrib.rs @@ -171,9 +171,8 @@ impl Reconciler for Distrib { } } - #[must_use] pub fn error_policy(dist: Arc, error: &Error, ctx: Arc) -> Action { - warn!("reconcile failed: {:?}", error); + warn!("reconcile failed for {:?}: {:?}", dist.metadata.name, error); ctx.metrics.dist_reconcile_failure(&dist, error); Action::requeue(Duration::from_secs(5 * 60)) } diff --git a/operator/src/events.rs b/operator/src/events.rs index d363687..fdf4787 100644 --- a/operator/src/events.rs +++ b/operator/src/events.rs @@ -21,6 +21,16 @@ use k8s_openapi::api::core::v1::ObjectReference; } } +#[must_use] pub fn from_check(src_type: &str, src_name: &String, action: String, note: Option) -> Event { + Event { + type_: EventType::Normal, + reason: format!("Reconciling `{}` {}", src_name, src_type), + note, + action, + secondary: None, + } +} + #[must_use] pub fn from_update(src_type: &str, src_name: &String, child_type: &str, child_name: &String, child: Option) -> Event { Event { type_: EventType::Normal, diff --git a/operator/src/install.rs b/operator/src/install.rs index fc6c158..9517a3f 100644 --- a/operator/src/install.rs +++ b/operator/src/install.rs @@ -14,7 +14,7 @@ use kube::{ //use base64::{Engine as _, engine::general_purpose}; use std::sync::Arc; use tokio::time::Duration; -use tracing::{Span, debug, field, info, instrument, warn}; +use tracing::{Span, debug, field, info, instrument, warn, error}; use async_trait::async_trait; pub use k8s::install::{Install,InstallStatus, STATUS_INSTALLED}; pub use k8s::distrib::{Distrib, ComponentDependency}; @@ -64,6 +64,10 @@ impl Reconciler for Install { let bootstrap = jobs.get("vynil-bootstrap").await.unwrap(); if let Some(status) = bootstrap.status { if status.completion_time.is_none() { + warn!("Will not trigger auto-install before the bootstrap job is completed, requeue"); + recorder.publish( + events::from_check("Install", &name, "Bootstrap in progress, requeue".to_string(), None) + ).await.map_err(Error::KubeError)?; return Ok(Action::requeue(Duration::from_secs(60))) } } @@ -72,6 +76,10 @@ impl Reconciler for Install { // Validate that the requested package exist in that distrib if ! dist.have_component(self.spec.category.as_str(), self.spec.component.as_str()) { self.update_status_missing_component(client, OPERATOR, vec!(format!("{:} - {:} is not known from {:?} distribution", self.spec.category.as_str(), self.spec.component.as_str(), dist_name))).await.map_err(Error::KubeError)?; + warn!("Missing component for {ns}.{name}"); + recorder.publish( + events::from_check("Install", &name, "Missing component".to_string(), Some(format!("{:} - {:} is not known from {:?} distribution", self.spec.category.as_str(), self.spec.component.as_str(), dist_name))) + ).await.map_err(Error::KubeError)?; if dist.status.is_some() { return Err(Error::IllegalInstall) } else { // the dist is not yet updated, wait for it for 60s @@ -144,6 +152,11 @@ impl Reconciler for Install { } } if ! missing.is_empty() { + let note = missing[0].clone(); + warn!("Missing dependencies for {ns}.{name}: {note}"); + recorder.publish( + events::from_check("Install", &name, "Missing dependencies".to_string(), Some(note)) + ).await.map_err(Error::KubeError)?; self.update_status_missing_component(client, OPERATOR, missing).await.map_err(Error::KubeError)?; if should_fail { return Err(Error::IllegalInstall) @@ -153,7 +166,6 @@ impl Reconciler for Install { } // Use provided check script if comp.check.is_some() { - info!("Starting the check script"); let check = comp.check.clone().unwrap(); let mut script = script::Script::from_str(&check, script::new_base_context( self.spec.category.clone(), @@ -163,16 +175,25 @@ impl Reconciler for Install { )); let stage = "check".to_string(); let errors = match script.run_pre_stage(&stage) { - Ok(d) => Vec::new(), + Ok(_d) => Vec::new(), Err(e) => { let mut missing: Vec = Vec::new(); - missing.push(format!("{e}",e)); + missing.push(format!("{e}")); missing } }; if ! errors.is_empty() { - self.update_status_missing_component(client, OPERATOR, errors).await.map_err(Error::KubeError)?; + let note = errors[0].clone(); + warn!("Validation script failed for {ns}.{name}: {note}"); + recorder.publish( + events::from_check("Install", &name, "Validation failed".to_string(), Some(note)) + ).await.map_err(Error::KubeError)?; + self.update_status_check_failed(client, OPERATOR, errors).await.map_err(Error::KubeError)?; return Ok(Action::requeue(Duration::from_secs(60))) + } else { + recorder.publish( + events::from_check("Install", &name, "Validation succeed".to_string(), None) + ).await.map_err(Error::KubeError)?; } } let hashedself = crate::jobs::HashedSelf::new(ns.as_str(), name.as_str(), self.options_digest().as_str(), self.spec.distrib.as_str(), &comp.commit_id); @@ -191,32 +212,32 @@ impl Reconciler for Install { info!("Creating {agent_name} Job"); self.update_status_agent_started(client, OPERATOR).await.map_err(Error::KubeError)?; let job = jobs.create_install(agent_name.as_str(), &agent_job, action, name.as_str(), ns.as_str()).await.unwrap(); - debug!("Sending event {agent_name} Job"); recorder.publish( events::from_create("Install", &name, "Job", &job.name_any(), Some(job.object_ref(&()))) ).await.map_err(Error::KubeError)?; - debug!("Waiting {agent_name} to finish Job"); jobs.wait_max(agent_name.as_str(),2*60).await.map_err(Error::WaitError)?.map_err(Error::JobError)?; - debug!("Waited {agent_name} OK"); } else { - info!("Patching {agent_name} Job"); - let _job = match jobs.apply_install(agent_name.as_str(), &agent_job, action, name.as_str(), ns.as_str()).await {Ok(j)=>j,Err(_e)=>{ + let job = match jobs.apply_install(agent_name.as_str(), &agent_job, action, name.as_str(), ns.as_str()).await {Ok(j)=>j,Err(_e)=>{ let job = jobs.get(agent_name.as_str()).await.unwrap(); recorder.publish( - events::from_delete("plan", &name, "Job", &job.name_any(), Some(job.object_ref(&()))) + events::from_delete("Install", &name, "Job", &job.name_any(), Some(job.object_ref(&()))) ).await.map_err(Error::KubeError)?; jobs.delete(agent_name.as_str()).await.unwrap(); + error!("Recreating {agent_name} Job"); + recorder.publish( + events::from_create("Install", &name, "Job", &job.name_any(), Some(job.object_ref(&()))) + ).await.map_err(Error::KubeError)?; jobs.create_install(agent_name.as_str(), &agent_job, action, name.as_str(), ns.as_str()).await.unwrap() }}; - // TODO: Detect if the job changed after the patch (or event better would change prior) - // TODO: Send a patched event if changed - /*debug!("Sending event for {plan_name} to finish Job"); - recorder.publish( - events::from_patch("Install", &name, "Job", &_job.name_any(), Some(_job.object_ref(&()))) - ).await.map_err(Error::KubeError)?;*/ - debug!("Waiting {agent_name} to finish Job"); - jobs.wait_max(agent_name.as_str(),2*60).await.map_err(Error::WaitError)?.map_err(Error::JobError)?; - debug!("Waited {agent_name} OK"); + if let Some(status) = job.status { + if status.completion_time.is_none() { + error!("Waiting after {agent_name} Job"); + recorder.publish( + events::from_check("Install", &name, "Bootstrap in progress, requeue".to_string(), None) + ).await.map_err(Error::KubeError)?; + jobs.wait_max(agent_name.as_str(),2*60).await.map_err(Error::WaitError)?.map_err(Error::JobError)?; + } + } } Ok(Action::requeue(Duration::from_secs(5 * 60))) } @@ -295,7 +316,7 @@ impl Reconciler for Install { } #[must_use] pub fn error_policy(inst: Arc, error: &Error, ctx: Arc) -> Action { - warn!("reconcile failed: {:?}", error); + warn!("reconcile failed for '{:?}.{:?}': {:?}", inst.metadata.namespace, inst.metadata.name, error); ctx.metrics.inst_reconcile_failure(&inst, error); Action::requeue(Duration::from_secs(5 * 60)) } diff --git a/operator/src/lib.rs b/operator/src/lib.rs index 4f36a90..8a43529 100644 --- a/operator/src/lib.rs +++ b/operator/src/lib.rs @@ -13,7 +13,7 @@ pub enum Error { KubeError(#[source] kube::Error), #[error("check Error: {0}")] - CheckError(#[source] k8s::Error), + CheckError(#[source] package::Error), #[error("Kube wait job Error: {0}")] WaitError(#[source] tokio::time::error::Elapsed), diff --git a/package/Cargo.toml b/package/Cargo.toml index aac2e08..e76bbf4 100644 --- a/package/Cargo.toml +++ b/package/Cargo.toml @@ -8,15 +8,16 @@ license = " BSD-3-Clause" [dependencies] k8s = { path = "../k8s" } anyhow = "1.0.69" -rhai = { version = "1.12.0", features = ["serde"] } +rhai = { version = "1.12.0", features = ["sync", "serde"] } serde = { version = "1.0.152", features = ["derive"] } -log = "0.4.17" +tracing = "0.1.37" regex = "1.7.3" futures = "0.3.25" indexmap = "1.9.3" serde_json = "1.0.95" handlebars = "4.3.6" schemars = { version = "0.8.11", features = ["chrono"] } +tokio = { version = "1.23.0", features = ["macros", "rt-multi-thread"] } [lib] name = "package" diff --git a/package/src/lib.rs b/package/src/lib.rs index a451e71..1ffec6e 100644 --- a/package/src/lib.rs +++ b/package/src/lib.rs @@ -2,4 +2,5 @@ pub mod shell; pub mod script; pub mod terraform; pub mod template; -pub use k8s::yaml; \ No newline at end of file +pub use k8s::yaml; +pub use anyhow::Error; \ No newline at end of file diff --git a/package/src/script.rs b/package/src/script.rs index 1335b46..cd507b9 100644 --- a/package/src/script.rs +++ b/package/src/script.rs @@ -1,10 +1,11 @@ -use rhai::{Engine, Scope, Module, RhaiNativeFunc}; +use rhai::{Engine, Scope, Module/*, RhaiNativeFunc */}; use std::{process, path::{PathBuf, Path}}; use anyhow::{Result, bail}; -use core::any::Any; +//use core::any::Any; use crate::shell; -use k8s::{get_client,handlers::{SecretHandler,DistribHandler,InstallHandler,IngressHandler}}; +use k8s::{Client, get_client,handlers::{DistribHandler, Ingress, IngressHandler, InstallHandler, SecretHandler, Secret, CustomResourceDefinitionHandler, CustomResourceDefinition},install::Install, distrib::Distrib}; pub use rhai::ImmutableString; +use tokio::runtime::Handle; pub fn new_base_context(category:String, component:String, instance:String, config:&serde_json::Map) -> Scope<'static> { let json = serde_json::to_string(config).unwrap(); @@ -23,62 +24,137 @@ pub fn new_context(category:String, component:String, instance:String, src:Strin s } +#[derive(Debug)] pub struct Script { pub engine: Engine, ctx: Scope<'static> } + +fn add_to_engine(engine: &mut Engine, code: &str, ctx: Scope<'static>) { + match engine.compile(code) {Ok(ast) => { + match Module::eval_ast_as_new(ctx, &ast,&engine) {Ok(module) => { + engine.register_global_module(module.into()); + }, Err(e) => {tracing::error!("Parsing {code} failed with: {e:}");},}; + }, Err(e) => {tracing::error!("Loading {code} failed with: {e:}")},}; +} + +fn create_engine(client: &Client) -> Engine { + let mut e = Engine::new(); + // Logging + e.register_fn("log_debug", |s:ImmutableString| tracing::debug!("{s}")); + e.register_fn("log_info", |s:ImmutableString| tracing::info!("{s}")); + e.register_fn("log_warn", |s:ImmutableString| tracing::warn!("{s}")); + e.register_fn("log_error", |s:ImmutableString| tracing::error!("{s}")); + // lancement de commande shell + e.register_fn("shell", |s:ImmutableString| { + shell::run_log_check(&format!("{s}")); + }); + e.register_fn("sh_value", |s:ImmutableString| { + shell::get_output(&format!("{s}")).unwrap() + }); + let cli = client.clone(); + e.register_fn("have_crd", move |name:ImmutableString| -> bool { + let cl = cli.clone(); + tokio::task::block_in_place(|| {Handle::current().block_on(async move { + let mut handle = CustomResourceDefinitionHandler::new(&cl); + handle.have(&name).await + })}) + }); + let cli = client.clone(); + e.register_fn("get_crd", move |name:ImmutableString| -> CustomResourceDefinition { + let cl = cli.clone(); + tokio::task::block_in_place(|| {Handle::current().block_on(async move { + let mut handle = CustomResourceDefinitionHandler::new(&cl); + handle.get(&name).await.unwrap() + })}) + }); + let cli = client.clone(); + e.register_fn("have_distrib", move |name:ImmutableString| -> bool { + let cl = cli.clone(); + tokio::task::block_in_place(|| {Handle::current().block_on(async move { + let mut handle = DistribHandler::new(&cl); + handle.have(&name).await + })}) + }); + let cli = client.clone(); + e.register_fn("get_distrib", move |name:ImmutableString| -> Distrib { + let cl = cli.clone(); + tokio::task::block_in_place(|| {Handle::current().block_on(async move { + let mut handle = DistribHandler::new(&cl); + handle.get(&name).await.unwrap() + })}) + }); + let cli: Client = client.clone(); + e.register_fn("have_install", move |ns:ImmutableString, name:ImmutableString| -> bool { + let cl = cli.clone(); + tokio::task::block_in_place(|| {Handle::current().block_on(async move { + let mut handle = InstallHandler::new(&cl, &ns); + let ret = handle.have(&name).await; + ret + })}) + }); + let cli: Client = client.clone(); + e.register_fn("get_install", move |ns:ImmutableString, name:ImmutableString| -> Install { + let cl = cli.clone(); + tokio::task::block_in_place(|| {Handle::current().block_on(async move { + let mut handle = InstallHandler::new(&cl, &ns); + let ret = handle.get(&name).await.unwrap(); + ret + })}) + }); + let cli: Client = client.clone(); + e.register_fn("have_ingress", move |ns:ImmutableString, name:ImmutableString| -> bool { + let cl = cli.clone(); + tokio::task::block_in_place(|| {Handle::current().block_on(async move { + let mut handle = IngressHandler::new(&cl, &ns); + handle.have(&name).await + })}) + }); + let cli: Client = client.clone(); + e.register_fn("get_ingress", move |ns:ImmutableString, name:ImmutableString| -> Ingress { + let cl = cli.clone(); + tokio::task::block_in_place(|| {Handle::current().block_on(async move { + let mut handle = IngressHandler::new(&cl, &ns); + handle.get(&name).await.unwrap() + })}) + }); + let cli = client.clone(); + e.register_fn("have_secret", move |ns:ImmutableString, name:ImmutableString| -> bool { + let cl = cli.clone(); + tokio::task::block_in_place(|| {Handle::current().block_on(async move { + let mut handle = SecretHandler::new(&cl, &ns); + handle.have(&name).await + })}) + }); + let cli = client.clone(); + e.register_fn("get_secret", move |ns:ImmutableString, name:ImmutableString| -> Secret { + let cl = cli.clone(); + tokio::task::block_in_place(|| {Handle::current().block_on(async move { + let mut handle = SecretHandler::new(&cl, &ns); + handle.get(&name).await.unwrap() + })}) + }); + 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 +} + impl Script { pub fn new(ctx: Scope<'static>) -> Script { - let mut e = Engine::new(); - // Logging - e.register_fn("log_debug", |s:ImmutableString| log::debug!("{s}")); - e.register_fn("log_info", |s:ImmutableString| log::info!("{s}")); - e.register_fn("log_warn", |s:ImmutableString| log::warn!("{s}")); - e.register_fn("log_error", |s:ImmutableString| log::error!("{s}")); - // lancement de commande shell - e.register_fn("shell", |s:ImmutableString| { - shell::run_log_check(&format!("{s}")); - }); - e.register_fn("sh_value", |s:ImmutableString| { - shell::get_output(&format!("{s}")).unwrap() + let cl = futures::executor::block_on(async move { + get_client().await }); - e.register_fn("have_distrib", |name:ImmutableString| -> bool { - futures::executor::block_on(async move { - let mut handle = DistribHandler::new(get_client().await); - handle.have(&name).await - }) - }); - e.register_fn("have_install", |ns:ImmutableString, name:ImmutableString| -> bool { - futures::executor::block_on(async move { - let mut handle = InstallHandler::new(get_client().await, &ns); - handle.have(&name).await - }) - }); - e.register_fn("have_ingress", |ns:ImmutableString, name:ImmutableString| -> bool { - futures::executor::block_on(async move { - let mut handle = IngressHandler::new(get_client().await, &ns); - handle.have(&name).await - }) - }); - e.register_fn("have_secret", |ns:ImmutableString, name:ImmutableString| -> bool { - futures::executor::block_on(async move { - let mut handle = SecretHandler::new(get_client().await, &ns); - handle.have(&name).await - }) - }); - // TODO: Add an http client (download/get/post/put) - // TODO: Add a kubectl wrapper - - Script {engine: e, ctx} + Script {engine: create_engine(&cl), ctx} } pub fn from(file:&PathBuf, ctx: Scope<'static>) -> Script { let mut script = Self::new(ctx.clone()); if Path::new(&file).is_file() { let str = file.as_os_str().to_str().unwrap(); - let ast = match script.engine.compile_file(str.into()) {Ok(d) => d, Err(e) => {log::error!("Loading {str} failed with: {e:}");process::exit(1)},}; + let ast = match script.engine.compile_file(str.into()) {Ok(d) => d, Err(e) => {tracing::error!("Loading {str} failed with: {e:}");process::exit(1)},}; let module = match Module::eval_ast_as_new(ctx, &ast,&script.engine) { - Ok(d) => d, Err(e) => {log::error!("Parsing {str} failed with: {e:}");process::exit(1)}, + Ok(d) => d, Err(e) => {tracing::error!("Parsing {str} failed with: {e:}");process::exit(1)}, }; script.engine.register_global_module(module.into()); } @@ -101,11 +177,7 @@ impl Script { pub fn from_str(code: &str, ctx: Scope<'static>) -> Script { let mut script = Self::new(ctx.clone()); - match script.engine.compile(code) {Ok(ast) => { - match Module::eval_ast_as_new(ctx, &ast,&script.engine) {Ok(module) => { - script.engine.register_global_module(module.into()); - }, Err(e) => {log::error!("Parsing {code} failed with: {e:}");},}; - }, Err(e) => {log::error!("Loading {code} failed with: {e:}")},}; + add_to_engine(&mut script.engine, code, ctx.clone()); script } @@ -113,9 +185,9 @@ impl Script { self.ctx = ctx; } - pub fn register+ 'static>(&mut self, name: &str, func: F) { + /*pub fn register+ SendSync + 'static>(&mut self, name: &str, func: F) { self.engine.register_fn(name, func); - } + }*/ fn run_fn(&mut self, func: &str) -> Result<()> { let cmd = format!("let x = {func}();x!=false"); diff --git a/package/src/shell.rs b/package/src/shell.rs index 64a77c0..e7037cd 100644 --- a/package/src/shell.rs +++ b/package/src/shell.rs @@ -19,10 +19,10 @@ pub fn run_log(command: &String) -> Result<()> { let stdout = String::from_utf8(output.stdout).unwrap(); let stderr = String::from_utf8(output.stderr).unwrap(); if !stdout.is_empty() { - log::info!("{}", stdout.trim()); + tracing::info!("{}", stdout.trim()); } if !stderr.is_empty() { - log::warn!("{}", stderr.trim()); + tracing::warn!("{}", stderr.trim()); } if ! output.status.success() { bail!("The command {:?} failed.", command); @@ -32,7 +32,7 @@ pub fn run_log(command: &String) -> Result<()> { pub fn run_log_check(command: &String) { match run_log(command) {Ok(_) => {}, Err(e) => { - log::error!("{e}"); + tracing::error!("{e}"); process::exit(1); }} } @@ -41,7 +41,7 @@ pub fn get_output(command: &String) -> Result { let output = run(command); let stderr = String::from_utf8(output.stderr).unwrap(); if !stderr.is_empty() { - log::warn!("{}", stderr.trim()); + tracing::warn!("{}", stderr.trim()); } if ! output.status.success() { bail!("The command {:?} failed.", command); diff --git a/package/src/terraform.rs b/package/src/terraform.rs index ea2180b..7a84a5b 100644 --- a/package/src/terraform.rs +++ b/package/src/terraform.rs @@ -342,7 +342,7 @@ variable \"instance\" {{ let output = match shell::get_output(&format!("echo 'jsondecode({:?})'|terraform console",str)) {Ok(d) => d, Err(e) => {bail!("{e}")}}; if yaml.tfaddtype.is_some() && *yaml.tfaddtype.as_ref().unwrap() { let typed = yaml.get_tf_type(name); - log::debug!("{}({})={}", name, typed, output); + tracing::debug!("{}({})={}", name, typed, output); content += format!("variable \"{}\" {{ default = {} type = {} @@ -367,7 +367,7 @@ pub fn gen_tfvars(dest_dir: &PathBuf, config:&serde_json::Map d, Err(e) => {bail!("{e}")}}; - log::debug!("{}={}", name, output); + tracing::debug!("{}={}", name, output); content += format!("{} = {} ", name, output).as_str(); }