improves workings

Signed-off-by: Jochen Maes <jochen@sejo-it.be>
This commit is contained in:
Jochen Maes 2023-10-27 07:08:18 +02:00
parent 55fc77b728
commit 71ee0aaab0
11 changed files with 168 additions and 209 deletions

44
.drone.yml Normal file
View File

@ -0,0 +1,44 @@
---
kind: pipeline
type: docker
name: "Mouse release"
platform:
arch: amd64
trigger:
event:
- tag
volumes:
- name: dockersock
host:
path: /var/run/docker.sock
- name: artifacts
temp: {}
steps:
- name: build amd64
image: rust:1.73
commands:
- rustup target add x86_64-unknown-linux-musl
- rustup target add aarch64-unknown-linux-musl
- cargo build --target x86_64-unknown-linux-musl --bins -r
- cargo build --target aarch64-unknown-linux-musl --bins -r
- cp target/x86_64-unknown-linux-musl/release/mouse /artifcats/mouse-amd64
- cp target/aarch64-unknown-linux-musl/release/mouse /artifcats/mouse-arm64
volumes:
- name: artifacts
path: /artifcats/
- name: create gitea release
image: plugins/gitea-release
settings:
api_key:
from_secret: gitea_api_key
base_url: https://gitea.sejo-it.be
checksum: sha265
files: /artifacts/*
volumes:
- name: artifacts
path: /artifcats/

20
Cargo.lock generated
View File

@ -52,9 +52,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.4.6" version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -62,9 +62,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.4.6" version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -74,9 +74,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.4.2" version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@ -86,9 +86,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.5.1" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
@ -125,9 +125,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.1" version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
[[package]] [[package]]
name = "heck" name = "heck"

View File

@ -8,7 +8,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
clap = { version = "4.4.6", features = ["derive"] } clap = { version = "4.4.7", features = ["derive"] }
csv = "1.3.0" csv = "1.3.0"
serde = { version = "1.0.189", features = ["std", "derive"] } serde = { version = "1.0.189", features = ["std", "derive"] }
serde_json = "1.0.107" serde_json = "1.0.107"

View File

@ -1,23 +1,28 @@
use crate::types::fact::{FactData, Fact}; use crate::types::fact::Fact;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::env; use std::env;
pub struct EnvironmentData {} pub struct EnvironmentData {}
impl EnvironmentData {}
impl EnvironmentData { #[derive(Serialize, Deserialize)]
pub struct EnvironmentValue {
key: String,
value: String,
time_set: u128,
} }
impl Fact for EnvironmentData { impl Fact for EnvironmentData {
fn gather(&self) -> Vec<FactData>{ fn gather(&self) -> String {
let mut vfd: Vec<FactData> = vec![]; let mut outmap: Vec<Value> = vec![];
let time_set = self.get_epoch_ms();
for (key, value) in env::vars() { for (key, value) in env::vars() {
let fd = FactData{ let entry = json!({
name: String::from("env_") + &key, "key": &key,
value: value, "value": value,
time_set: time_set });
}; outmap.append(&mut vec![entry]);
vfd.push(fd);
} }
return vfd; serde_json::to_string(&outmap).unwrap()
} }
} }

View File

@ -1,107 +1,29 @@
use crate::{types::fact::{Fact, FactData}, util::command::get_result_as_string}; use crate::{types::fact::Fact, util::command::get_result_as_string};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Serialize, Deserialize)]
pub struct IPData {} pub struct IPData {}
impl IPData {} impl IPData {}
#[derive(Serialize, Deserialize)]
struct IpAddrInfo {
family: String,
local: String,
prefixlen: i32,
broadcast: Option<String>,
scope: String,
dynamic: Option<bool>,
noprefixroute: Option<bool>,
label: Option<String>,
valid_life_time: i64,
preferred_life_time: i64,
}
#[derive(Serialize, Deserialize)]
struct IpAddr {
ifindex: i32,
ifname: String,
flags: Vec<String>,
mtu: i32,
qdisc: String,
operstate: String,
group: String,
txqlen: i32,
link_type: String,
address: Option<String>,
broadcast: Option<String>,
addr_info: Vec<IpAddrInfo>,
}
impl Fact for IPData { impl Fact for IPData {
fn gather(&self) -> Vec<FactData> { fn gather(&self) -> String {
let mut vfd: Vec<FactData> = vec![]; let res = get_result_as_string("ip", vec!["-j", "addr"]);
let time_set = self.get_epoch_ms(); let jsonres: Value = serde_json::from_str(res.as_str()).unwrap();
let ips: Vec<IpAddr> = serde_json::from_str(get_result_as_string("ip", vec!["-j", "addr"]).as_str()).unwrap(); let out = json!({ "ip_addr": jsonres });
for ip in ips { out.to_string()
if ip.ifname != "lo" {
for addr_info in ip.addr_info {
let fd = FactData {
name: format!("ip_addr_{}_{}_addr", ip.ifname, addr_info.family),
value: addr_info.local,
time_set: time_set,
};
vfd.push(fd);
vfd.push(FactData {
name: format!("ip_addr_{}_{}_prefixlen", ip.ifname, addr_info.family),
value: addr_info.prefixlen.to_string(),
time_set: time_set,
});
if addr_info.label.is_some() {
vfd.push(FactData {
name: format!("ip_addr_{}_{}_label", ip.ifname, addr_info.family),
value: addr_info.label.unwrap(),
time_set: time_set,
});
}
if addr_info.broadcast.is_some() {
vfd.push(FactData {
name: format!("ip_addr_{}_{}_broadcast", ip.ifname, addr_info.family),
value: addr_info.broadcast.unwrap(),
time_set: time_set,
});
}
if ip.address.is_some() {
vfd.push(FactData {
name: format!("ip_addr_{}_macaddr", ip.ifname),
value: ip.address.clone().unwrap(),
time_set: time_set,
})
}
}
}
}
return vfd;
} }
} }
pub struct IPRouteData {}
impl IPRouteData {}
#[derive(Serialize, Deserialize)] impl Fact for IPRouteData {
struct IpRoute { fn gather(&self) -> String {
dst: String, let res = get_result_as_string("ip", vec!["-j", "route"]);
gateway: Option<String>, let jsonres: Value = serde_json::from_str(res.as_str()).unwrap();
dev: String, let out = json!({
protocol: Option<String>, "ip_route": jsonres
metric: Option<i32>, });
flags: Vec<String>, out.to_string()
scope: String,
prefsrc: String,
}
impl Fact for IpRoute {
fn gather(&self) -> Vec<FactData> {
let mut vfd: Vec<FactData> = vec![];
let time_set = self.get_epoch_ms();
let routes: Vec<IpRoute> = serde_json::from_str(get_result_as_string("ip", vec!["-j", "route"]).as_str()).unwrap();
return vfd;
} }
} }

View File

@ -1,2 +1,2 @@
pub mod environment; pub mod environment;
pub mod ip; pub mod ip;

View File

@ -1,14 +1,16 @@
mod types;
mod gatherers; mod gatherers;
mod types;
mod util; mod util;
use std::collections::HashSet; #[macro_use]
use crate::types::fact::{Fact, FactData}; extern crate serde_json;
use crate::gatherers::environment::EnvironmentData;
use crate::gatherers::ip::IPData;
use clap::Parser;
use csv::Writer;
use crate::gatherers::environment::EnvironmentData;
use crate::gatherers::ip::{IPData, IPRouteData};
use crate::types::fact::Fact;
use clap::Parser;
use serde_json::Value;
use std::collections::{HashMap, HashSet};
#[derive(Parser)] #[derive(Parser)]
#[clap(author, version, about, long_about = None)] #[clap(author, version, about, long_about = None)]
@ -17,69 +19,61 @@ struct Args {
#[arg(short, long)] #[arg(short, long)]
gatherer: Option<Vec<String>>, gatherer: Option<Vec<String>>,
/// Output format, yaml, json are supported /// Output format, yaml, json are supported
#[arg(short, long, default_value="yaml")] #[arg(short, long, default_value = "yaml")]
output: String, output: String,
} }
fn gather_all() -> Vec<FactData>{ fn gather_list(gatherers: HashSet<String>, output: &str) -> String {
let mut data: Vec<FactData> = vec![]; let mut outmap: HashMap<String, Value> = HashMap::new();
data.append(&mut gather(&EnvironmentData{}));
data.append(&mut gather(&IPData{}));
return data;
}
fn gather_list(gatherers: HashSet<String>) -> Vec<FactData> {
let mut data: Vec<FactData> = vec![];
for g in gatherers { for g in gatherers {
match g.as_str() { match g.as_str() {
"all" => for d in gather_all(){ "env" => {
data.push(d); outmap.insert(
}, "environment".to_string(),
"env" => for d in gather(&EnvironmentData{}) { serde_json::from_str(&EnvironmentData {}.gather()).unwrap(),
data.push(d); );
}, }
"ip" => for d in gather(&IPData{}) { "ipaddr" => {
data.push(d); outmap.insert(
"ipaddr".to_string(),
serde_json::from_str(&IPData {}.gather()).unwrap(),
);
}
"iproute" => {
outmap.insert(
"iproute".to_string(),
serde_json::from_str(&IPRouteData {}.gather()).unwrap(),
);
}
x => {
println!("unknown gatherer: {x}");
} }
_ => for d in gather_all() {
data.push(d);
}
}; };
} }
return data; match output {
} "json" => serde_json::to_string(&outmap).unwrap(),
fn gather (t: &dyn Fact) -> Vec<FactData> { "yaml" => serde_yaml::to_string(&outmap).unwrap(),
return t.gather(); x => format!("Unknown output format {x}"),
}
} }
fn main() { fn main() {
let args = Args::parse(); let args = Args::parse();
let mut data:Vec<FactData> = vec![]; let output_format = match args.output.as_str() {
"json" => "json",
if args.gatherer.is_none() { "yaml" => "yaml",
data.append(&mut gather_all()); _ => "yaml",
} else {
// sanitize the gatherer list
let ugatherers: HashSet<String> = match &args.gatherer {
Some(x) => x.into_iter().map(|name| name).cloned().collect::<HashSet<String>>(),
None => HashSet::<String>::new(),
};
data.append(&mut gather_list(ugatherers));
}
match args.output.as_str() {
"json" => {
let serialized_json = serde_json::to_string(&data).unwrap();
println!("{}", serialized_json);
},
"csv" => {
let mut writer = Writer::from_writer(vec![]);
for fd in data {
writer.serialize(fd).unwrap();
}
println!("{}", String::from_utf8(writer.into_inner().unwrap()).unwrap());
}
&_ => {
let serialized_yaml = serde_yaml::to_string(&data).unwrap();
println!("{}", serialized_yaml);
},
}; };
let all: HashSet<String> = HashSet::from([
"env".to_string(),
"ipaddr".to_string(),
"iproute".to_string(),
]);
let data_output: String = match args.gatherer {
None => gather_list(all, output_format),
Some(x) => {
let gatherers: HashSet<String> = x.into_iter().collect::<HashSet<String>>();
gather_list(gatherers, output_format)
}
};
println!("{}", data_output);
} }

View File

@ -1,20 +1,12 @@
use std::time::{UNIX_EPOCH, SystemTime}; use std::time::{SystemTime, UNIX_EPOCH};
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)] pub trait Fact: Send + Sync {
pub struct FactData { fn gather(&self) -> String;
pub name: String,
pub value: String,
pub time_set: u128,
}
pub trait Fact : Send + Sync {
fn gather(&self) -> Vec<FactData>;
fn get_epoch_ms(&self) -> u128 { fn get_epoch_ms(&self) -> u128 {
SystemTime::now() SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
.unwrap() .unwrap()
.as_millis() .as_millis()
} }
} }

View File

@ -1 +1 @@
pub mod fact; pub mod fact;

View File

@ -10,33 +10,35 @@ fn get_output_from_command(cmd: &str, args: Vec<&str>) -> Output {
fn get_stdout_as_string(output: Output) -> String { fn get_stdout_as_string(output: Output) -> String {
return match String::from_utf8(output.stdout) { return match String::from_utf8(output.stdout) {
Ok(x) => x.strip_suffix("\n").unwrap().to_string(), Ok(x) => x.strip_suffix("\n").unwrap().to_string(),
Err(e) => e.to_string() Err(e) => e.to_string(),
} };
} }
fn get_stderr_as_string(output: Output) -> String { fn get_stderr_as_string(output: Output) -> String {
return match String::from_utf8(output.stderr) { return match String::from_utf8(output.stderr) {
Ok(x) => x.strip_suffix("\n").unwrap().to_string(), Ok(x) => x.strip_suffix("\n").unwrap().to_string(),
Err(e) => e.to_string() Err(e) => e.to_string(),
} };
} }
pub fn get_result_as_string(cmd: &str, args:Vec<&str>) -> String { pub fn get_result_as_string(cmd: &str, args: Vec<&str>) -> String {
let output = get_output_from_command(cmd, args); let output = get_output_from_command(cmd, args);
match output.status.code() { match output.status.code() {
Some(0) => get_stdout_as_string(output), Some(0) => get_stdout_as_string(output),
_ => get_stderr_as_string(output) _ => get_stderr_as_string(output),
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::util::command::{get_result_as_string}; use crate::util::command::get_result_as_string;
#[test] #[test]
fn test_get_output_from_command() { fn test_get_output_from_command() {
assert_eq!("Linux", get_result_as_string("uname", vec![])); assert_eq!("Linux", get_result_as_string("uname", vec![]));
assert_eq!("ls: cannot access '/dev/mouse': No such file or directory", assert_eq!(
get_result_as_string("ls", vec!["/dev/mouse",])) "ls: cannot access '/dev/mouse': No such file or directory",
get_result_as_string("ls", vec!["/dev/mouse",])
)
} }
} }

View File

@ -1 +1 @@
pub mod command; pub mod command;