This commit is contained in:
a-Sansara 2020-10-08 18:40:35 +02:00
commit 4d683e9201
9 changed files with 285 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
Cargo.lock
target/

15
Cargo.toml Normal file
View File

@ -0,0 +1,15 @@
[package]
name = "ykre"
version = "0.1.0"
authors = ["a-Sansara <dev]at[pluie]dot[org>"]
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"

31
README.md Normal file
View File

@ -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

BIN
dist/ykre_0.1.0_amd64.deb vendored Normal file

Binary file not shown.

29
src/main.rs Normal file
View File

@ -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(())
}

56
src/ykre.rs Normal file
View File

@ -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
";

67
src/ykre/app.rs Normal file
View File

@ -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<String> = 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::<u32>().unwrap();
if get_data(&mut buf, limit) {
return find(search, def, buf);
}
panic!(YKRE_ERROR_NOPIPE)
}
panic!(YKRE_ERROR_INVALID)
}

17
src/ykre/pipin.rs Normal file
View File

@ -0,0 +1,17 @@
use std::{io, thread, time, sync::mpsc};
use std::sync::mpsc::Receiver;
pub fn spawn_stdin() -> Receiver<String> {
let (tx, rx) = mpsc::channel::<String>();
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);
}

68
src/ykre/yaml.rs Normal file
View File

@ -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<yaml_rust::Yaml>
}
impl YamlDocument {
fn loaded_from(content: String, source: Option<String>) -> 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<usize> {
let mut index = 0;
let mut rs: Option<usize> = 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<usize> {
let mut v: Vec<usize> = 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
}
}