commit 4d683e9201721a0f874e2afc43e9b6798fc2f989 Author: a-Sansara Date: Thu Oct 8 18:40:35 2020 +0200 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e7caa9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +Cargo.lock +target/ diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..69987c4 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "ykre" +version = "0.1.0" +authors = ["a-Sansara "] +edition = "2018" +description = "Ykre is Yaml Kubernetes Resource Extractor" +license = "MIT" +readme = "README.md" +keywords = ["cli", "yaml", "kubernetes", "kustomize"] +categories = ["command-line-utilities"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +yaml-rust = "0.4" diff --git a/README.md b/README.md new file mode 100644 index 0000000..328a057 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# Ykre + +**Ykre** is a small cli program written in rust wich purpose is to extract from a list +of yaml documents, documents matching a specified condition. **Ykre** goal is to find +specific Kubernetes Resource from the kustomize output. +**Ykre** stands for **Y**aml **K**ubernetes **R**esources **E**ctractor. + +## usage +``` +ykre SEARCH [DEF] +``` + +## examples + +display content of the kubernetes resource 'myResourceName' +``` +cat kubresources.yaml | ykre 'myResourceName' +``` +write content of the kubernetes resource 'pv-dump' into a file +``` +kustomize build ./config/volumes/local | ykre 'pv-dump' > ./tmp.yaml +``` +retriew kubernetes pv resources matching 2Gi disk capacity +``` +kustomize build ./config/volumes/dev | ykre 2Gi spec.capacity.storage +``` + + +## License + +MIT diff --git a/dist/ykre_0.1.0_amd64.deb b/dist/ykre_0.1.0_amd64.deb new file mode 100644 index 0000000..92cc46a Binary files /dev/null and b/dist/ykre_0.1.0_amd64.deb differ diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..dcf3e83 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,29 @@ +extern crate yaml_rust; +pub mod ykre; + +use std::io; +use std::panic; + +const PANIC_BYPASS_FILE : &str = "src/ykre/pipin.rs"; +const PANIC_BYPASS_LINE : u32 = 9; + +fn main() -> io::Result<()> { + + panic::set_hook(Box::new(|_info| { + let mut bypass :bool = false; + if let Some(location) = &_info.location() { + bypass = location.file() == PANIC_BYPASS_FILE && location.line() == PANIC_BYPASS_LINE; + } + if !bypass { + //~ println!("{:?}", &_info); + if let Some(s) = &_info.payload().downcast_ref::<&str>() { + if s.len() > 1 { + println!("{}", s); + } + } + } + })); + + ykre::run(); + Ok(()) +} diff --git a/src/ykre.rs b/src/ykre.rs new file mode 100644 index 0000000..47ea9ed --- /dev/null +++ b/src/ykre.rs @@ -0,0 +1,56 @@ +mod yaml; +mod pipin; +mod app; + +pub use self::yaml::YamlDocument; +pub use self::pipin::{spawn_stdin, sleep}; +pub use self::app::{run}; + +const YKRE_LIMIT_ATTEMPT : u64 = 900; +const YKRE_SLEEP_START : u64 = 50; +const YKRE_SLEEP_DATA : u64 = 1; +const YKRE_SLEEP_NODATA : u64 = 1; +const YKRE_DEFAULT_DEF : &str = "metadata.name"; +const YKRE_ERROR_NOTFOUND : &str = r" + :: Ykre :: + + error : no match found for specified pattern +"; +const YKRE_ERROR_INVALID : &str = r" + :: Ykre :: + + error : invalid parameter. type ykre for usage +"; +const YKRE_ERROR_NOPIPE : &str = r" + :: Ykre :: + + error : you must send data using pipe +"; +const YKRE_HELP : &str = r" +----------------------------------------------------------------------------------- + :: Ykre :: v0.1 license : MIT - author : a-Sansara +----------------------------------------------------------------------------------- + + Ykre is a small program written in `rust` wich purpose is to extract from a list + of yaml documents, documents matching a specified condition. Ykre goal is to find + specific Kubernetes Resource from the kustomize output. + Ykre stands for Yaml Kubernetes Resources Ectractor. + + Usage : + + ykre SEARCH [DEF] + + you MUST use pipe with ykre command + + SEARCH - value of kubernetes resource name + DEF - the yaml node to look for + (default : 'metadata.name', dot is the separator for embed nodes) + + ex : + # display content of the kubernetes resource 'myResourceName' + cat kubresources.yaml | ykre 'myResourceName' + # write content of the kubernetes resource 'pv-dump' into a file + kustomize build ./config/volumes/local | ykre 'pv-dump' > ./tmp.yaml + # retriew kubernetes pv resources matching 2Gi disk capacity + kustomize build ./config/volumes/dev | ykre 2Gi spec.capacity.storage +"; diff --git a/src/ykre/app.rs b/src/ykre/app.rs new file mode 100644 index 0000000..b9d2cba --- /dev/null +++ b/src/ykre/app.rs @@ -0,0 +1,67 @@ +use std::panic; +use std::env; +use std::sync::mpsc::TryRecvError; + +use crate::ykre::*; + +pub fn find(search: &str, def: &str, data:String) { + let doc = YamlDocument::load_str(data); + let rs = doc.match_docs(&def, &search); + if !rs.is_empty() { + for i in rs.iter() { + println!("{}", doc.dump(*i)); + } + return + } + panic!(YKRE_ERROR_NOTFOUND); +} + +pub fn get_data(buf :&mut String, limit :u32) -> bool { + let stdin = spawn_stdin(); + let mut attempt = 0; + let mut has_data :bool = false; + sleep(YKRE_SLEEP_START); + while attempt < limit { + let rs = match stdin.try_recv() { + Ok(s) => s, + Err(TryRecvError::Empty) => String::from(""), + Err(TryRecvError::Disconnected) => String::from(""), + }; + if !rs.is_empty() { + *buf += &rs; + has_data = true; + } + else if has_data { + break; + } + attempt += 1; + sleep(match has_data { + true => YKRE_SLEEP_DATA, + _ => YKRE_SLEEP_NODATA, + }); + } + return has_data +} + +pub fn run() { + let args: Vec = env::args().collect(); + if args.len() == 1 { + println!("{}", YKRE_HELP); + return + } + else if args.len() > 1 && args.len() <= 3 { + + let mut buf :String = String::from(""); + let search :&str = &args[1]; + let def :&str = match args.len() { + 3 => &args[2], + _ => YKRE_DEFAULT_DEF, + }; + let limit : u32 = env::var("YKRE_LIMIT_ATTEMPT").unwrap_or(format!("{}", YKRE_LIMIT_ATTEMPT).to_owned()).parse::().unwrap(); + if get_data(&mut buf, limit) { + return find(search, def, buf); + } + panic!(YKRE_ERROR_NOPIPE) + } + panic!(YKRE_ERROR_INVALID) +} diff --git a/src/ykre/pipin.rs b/src/ykre/pipin.rs new file mode 100644 index 0000000..ebddaab --- /dev/null +++ b/src/ykre/pipin.rs @@ -0,0 +1,17 @@ +use std::{io, thread, time, sync::mpsc}; +use std::sync::mpsc::Receiver; + +pub fn spawn_stdin() -> Receiver { + let (tx, rx) = mpsc::channel::(); + let _child = thread::spawn(move || loop { + let mut buffer = String::new(); + io::stdin().read_line(&mut buffer).unwrap(); + tx.send(buffer).unwrap() + }); + rx +} + +pub fn sleep(millis: u64) { + let duration = time::Duration::from_millis(millis); + thread::sleep(duration); +} diff --git a/src/ykre/yaml.rs b/src/ykre/yaml.rs new file mode 100644 index 0000000..05fbfd9 --- /dev/null +++ b/src/ykre/yaml.rs @@ -0,0 +1,68 @@ +use yaml_rust::{YamlLoader, Yaml, YamlEmitter}; +use std::vec::Vec; + +#[derive(Debug)] +pub struct YamlDocument { + path: String, + content: String, + docs: Vec +} + +impl YamlDocument { + + fn loaded_from(content: String, source: Option) -> YamlDocument { + let docs = YamlLoader::load_from_str(&content).unwrap(); + let path = source.unwrap_or("".to_owned()); + YamlDocument { path, content, docs } + } + + pub fn load_str(content: String) -> YamlDocument { + Self::loaded_from(content, None) + } + + pub fn find_str(&self, path: &str, index: usize) -> Option<&str> { + let search = path.to_string(); + let v: Vec<&str> = search.split(".").collect(); + let mut node: &Yaml = &self.docs[index]; + for key in &v { + node = &node[*key]; + } + node.as_str().clone() + } + + pub fn dump(&self, index: usize) -> String { + let doc = &self.docs[index]; + let mut out_str = String::new(); + { + let mut emitter = YamlEmitter::new(&mut out_str); + emitter.dump(doc).unwrap(); + } + out_str + } + + pub fn match_doc(&self, search: &str, matched: &str) -> Option { + let mut index = 0; + let mut rs: Option = None; + while index < self.docs.len() { + match &self.find_str(search, index) { + Some(ref s) if s == &matched => rs = Some(index), + _ => (), + } + index += 1; + } + rs + } + + pub fn match_docs(&self, search: &str, matched: &str) -> Vec { + let mut v: Vec = Vec::new(); + let mut index = 0; + while index < self.docs.len() { + match &self.find_str(search, index) { + Some(ref s) if s == &matched => v.push(index), + _ => (), + } + index += 1; + } + v + } +}