diff --git a/Cargo.lock b/Cargo.lock index 8d17b62..f88cafe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -261,9 +261,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "argon2" @@ -289,9 +289,9 @@ dependencies = [ [[package]] name = "async-broadcast" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ "event-listener", "event-listener-strategy", @@ -1557,9 +1557,9 @@ dependencies = [ [[package]] name = "impl-more" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae21c3177a27788957044151cc2800043d127acaa460a47ebb9b84dfa2c6aa0" +checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" [[package]] name = "indexmap" @@ -1834,7 +1834,7 @@ dependencies = [ [[package]] name = "kuberest" -version = "1.1.5" +version = "1.2.0" dependencies = [ "actix-web", "anyhow", @@ -1861,7 +1861,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml", - "thiserror 2.0.8", + "thiserror 2.0.9", "tokio", "tower-test", "tracing", @@ -2257,7 +2257,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.8", + "thiserror 2.0.9", "ucd-trie", ] @@ -2420,7 +2420,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror 2.0.8", + "thiserror 2.0.9", "tokio", "tracing", ] @@ -2439,7 +2439,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.8", + "thiserror 2.0.9", "tinyvec", "tracing", "web-time", @@ -2461,9 +2461,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -2899,9 +2899,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" dependencies = [ "itoa", "memchr", @@ -3077,9 +3077,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.90" +version = "2.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" dependencies = [ "proc-macro2", "quote", @@ -3160,11 +3160,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.8" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f5383f3e0071702bf93ab5ee99b52d26936be9dedd9413067cbdcddcb6141a" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" dependencies = [ - "thiserror-impl 2.0.8", + "thiserror-impl 2.0.9", ] [[package]] @@ -3180,9 +3180,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.8" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f357fcec90b3caef6623a099691be676d033b40a058ac95d2a6ade6fa0c943" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 3ca7729..dcc2c11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kuberest" -version = "1.1.5" +version = "1.2.0" authors = ["Sébastien Huss "] edition = "2021" default-run = "controller" diff --git a/charts/kuberest/Chart.yaml b/charts/kuberest/Chart.yaml index 1c424dc..91938bf 100644 --- a/charts/kuberest/Chart.yaml +++ b/charts/kuberest/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: kuberest description: Allow to Control remote REST api endpoints from the confort of your cluster type: application -version: "1.1.5" -appVersion: "1.1.5" +version: "1.2.0" +appVersion: "1.2.0" diff --git a/deploy/crd/crd.yaml b/deploy/crd/crd.yaml index 76f7758..80daa6d 100644 --- a/deploy/crd/crd.yaml +++ b/deploy/crd/crd.yaml @@ -102,6 +102,10 @@ spec: required: - baseurl type: object + init: + description: A rhai pre-script to setup some complex variables before client setup + nullable: true + type: string inputs: description: List input source for Handlebars renders items: @@ -413,6 +417,7 @@ spec: - InputMissing - InputFailed - TemplateFailed + - InitScriptFailed - PreScriptFailed - PostScriptFailed - TeardownScriptFailed diff --git a/deploy/operator/deployment.yaml b/deploy/operator/deployment.yaml index d1babf1..2bfd308 100644 --- a/deploy/operator/deployment.yaml +++ b/deploy/operator/deployment.yaml @@ -8,7 +8,7 @@ metadata: labels: app: kuberest app.kubernetes.io/name: kuberest - app.kubernetes.io/version: "1.1.5" + app.kubernetes.io/version: "1.2.0" namespace: default automountServiceAccountToken: true --- @@ -102,7 +102,7 @@ metadata: labels: app: kuberest app.kubernetes.io/name: kuberest - app.kubernetes.io/version: "1.1.5" + app.kubernetes.io/version: "1.2.0" spec: type: ClusterIP ports: @@ -122,7 +122,7 @@ metadata: labels: app: kuberest app.kubernetes.io/name: kuberest - app.kubernetes.io/version: "1.1.5" + app.kubernetes.io/version: "1.2.0" spec: replicas: 1 selector: @@ -140,7 +140,7 @@ spec: {} containers: - name: kuberest - image: sebt3/kuberest:1.1.5 + image: sebt3/kuberest:1.2.0 imagePullPolicy: IfNotPresent securityContext: {} diff --git a/src/httphandler.rs b/src/httphandler.rs index e72dd23..2dc2c4e 100644 --- a/src/httphandler.rs +++ b/src/httphandler.rs @@ -676,12 +676,9 @@ impl RestClient { path: &str, key: &str, input: &Value, - use_slash: bool, ) -> Result { let full_path = if key.is_empty() { path.to_string() - } else if use_slash { - format!("{path}/{key}/") } else { format!("{path}/{key}") }; diff --git a/src/restendpoint.rs b/src/restendpoint.rs index 2d07ddb..c26ae84 100644 --- a/src/restendpoint.rs +++ b/src/restendpoint.rs @@ -249,6 +249,7 @@ pub enum ConditionsType { InputMissing, InputFailed, TemplateFailed, + InitScriptFailed, PreScriptFailed, PostScriptFailed, TeardownScriptFailed, @@ -309,6 +310,10 @@ impl ApplicationCondition { ) } + pub fn init_script_failed(message: &str) -> ApplicationCondition { + ApplicationCondition::new(message, ConditionsStatus::True, ConditionsType::InitScriptFailed) + } + pub fn pre_script_failed(message: &str) -> ApplicationCondition { ApplicationCondition::new(message, ConditionsStatus::True, ConditionsType::PreScriptFailed) } @@ -455,6 +460,8 @@ pub struct RestEndPointSpec { pub templates: Option>, /// List input source for Handlebars renders pub inputs: Option>, + /// A rhai pre-script to setup some complex variables before client setup + pub init: Option, /// Define the how the client should connect to the API endpoint(s) pub client: WebClient, /// A rhai pre-script to setup some complex variables @@ -824,6 +831,45 @@ impl RestEndPoint { rhai.set_dynamic("input", &values["input"]); rhai.set_dynamic("values", &values); + // Run the init script + if let Some(script) = self.spec.init.clone() { + let cnd = conditions.clone(); + values["init"] = rhai.eval(&script).unwrap_or_else(|e| { + conditions.push(ApplicationCondition::init_script_failed(&format!("{e:?}"))); + json!({}) + }); + // Validate that init-script went Ok + if cnd + .iter() + .any(|c| c.condition_type != ConditionsType::InputMissing) + { + let msg = "Init-script failed"; + Self::publish_warning( + &recorder, + String::from(msg), + format!("Found {} error(s) running the init-script", conditions.len()), + String::from("fail"), + ) + .await?; + conditions.push(ApplicationCondition::not_ready(msg)); + let new_status = Patch::Apply(json!({ + "apiVersion": "kuberest.solidite.fr/v1", + "kind": "RestEndPoint", + "status": RestEndPointStatus { conditions, generation: self.metadata.generation.unwrap_or(1), owned: self.owned(), owned_target: self.owned_target() } + })); + let ps = PatchParams::apply(RESTPATH_FINALIZER).force(); + let _o = restendpoints + .patch_status(&name, &ps, &new_status) + .await + .map_err(Error::KubeError)?; + return Ok(Action::requeue(Duration::from_secs( + self.spec.check_frequency.unwrap_or(15 * 60), + ))); + } + } + rhai.set_dynamic("init", &values["init"]); + rhai.set_dynamic("values", &values); + // Setup the httpClient let mut rest = RestClient::new(&template!( self.spec.client.baseurl.clone().as_str(), @@ -1056,7 +1102,6 @@ impl RestEndPoint { path.as_str(), &myself.key, &vals, - group.key_use_slash.unwrap_or(false), ) .unwrap_or_else(|e| { giveup = if let Error::MethodFailed(_, code, _) = e { @@ -1739,6 +1784,45 @@ impl RestEndPoint { } rhai.set_dynamic("input", &values["input"]); rhai.set_dynamic("values", &values); + + // Run the init script + if let Some(script) = self.spec.init.clone() { + let cnd = conditions.clone(); + values["init"] = rhai.eval(&script).unwrap_or_else(|e| { + conditions.push(ApplicationCondition::init_script_failed(&format!("{e:?}"))); + json!({}) + }); + // Validate that init-script went Ok + if cnd + .iter() + .any(|c| c.condition_type != ConditionsType::InputMissing) + { + let msg = "Init-script failed"; + Self::publish_warning( + &recorder, + String::from(msg), + format!("Found {} error(s) running the init-script", conditions.len()), + String::from("fail"), + ) + .await?; + conditions.push(ApplicationCondition::not_ready(msg)); + let new_status = Patch::Apply(json!({ + "apiVersion": "kuberest.solidite.fr/v1", + "kind": "RestEndPoint", + "status": RestEndPointStatus { conditions, generation: self.metadata.generation.unwrap_or(1), owned: self.owned(), owned_target: self.owned_target() } + })); + let ps = PatchParams::apply(RESTPATH_FINALIZER).force(); + let _o = restendpoints + .patch_status(&name, &ps, &new_status) + .await + .map_err(Error::KubeError)?; + return Ok(Action::requeue(Duration::from_secs( + self.spec.check_frequency.unwrap_or(15 * 60), + ))); + } + } + rhai.set_dynamic("init", &values["init"]); + rhai.set_dynamic("values", &values); } // Setup the httpClient