mirror of
https://github.com/house-of-vanity/rexec.git
synced 2025-07-07 16:54:07 +00:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
10345fbce6 | |||
ad2d36bb35 | |||
4f1657c37c | |||
a28d1a3a38 | |||
502d206dc7 | |||
e661349034 |
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -1043,7 +1043,7 @@ checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
|
||||
|
||||
[[package]]
|
||||
name = "rexec"
|
||||
version = "1.0.1"
|
||||
version = "1.0.3"
|
||||
dependencies = [
|
||||
"brace-expand",
|
||||
"clap 4.3.4",
|
||||
@ -1056,6 +1056,7 @@ dependencies = [
|
||||
"log",
|
||||
"massh",
|
||||
"regex",
|
||||
"tokio",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rexec"
|
||||
version = "1.0.1"
|
||||
version = "1.0.3"
|
||||
edition = "2021"
|
||||
description = "Parallel SSH executor"
|
||||
repository = "https://github.com/house-of-vanity/rexec"
|
||||
@ -12,6 +12,7 @@ authors = ["AB <gh@hexor.ru>"]
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
dns-lookup = "2.0.2"
|
||||
log = "0.4.0"
|
||||
env_logger = "0.10.0"
|
||||
|
79
src/main.rs
79
src/main.rs
@ -1,4 +1,13 @@
|
||||
use brace_expand::brace_expand;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs::read_to_string;
|
||||
use std::hash::Hash;
|
||||
use std::net::IpAddr;
|
||||
use std::process;
|
||||
|
||||
use clap::Parser;
|
||||
use colored::*;
|
||||
use dialoguer::Confirm;
|
||||
use dns_lookup::lookup_host;
|
||||
@ -7,17 +16,6 @@ use itertools::Itertools;
|
||||
use log::{error, info};
|
||||
use massh::{MasshClient, MasshConfig, MasshHostConfig, SshAuth};
|
||||
use regex::Regex;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::read_to_string;
|
||||
use std::hash::Hash;
|
||||
use std::net::IpAddr;
|
||||
|
||||
use std::process;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
// Define args
|
||||
#[derive(Parser, Debug)]
|
||||
@ -29,7 +27,11 @@ struct Args {
|
||||
#[arg(short, long, help = "Use known_hosts to build servers list")]
|
||||
known_hosts: bool,
|
||||
|
||||
#[arg(short, long, help = "Expression to build server list")]
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
help = "Expression to build server list. List and range expansion available. Example: 'web-[1:12]-io-{prod,dev}'"
|
||||
)]
|
||||
expression: String,
|
||||
|
||||
#[arg(short, long, help = "Command to execute on servers")]
|
||||
@ -75,19 +77,55 @@ fn read_known_hosts() -> Vec<Host> {
|
||||
result
|
||||
}
|
||||
|
||||
fn expand_expression(expr: &str) -> Vec<Host> {
|
||||
let mut result: Vec<Host> = Vec::new();
|
||||
fn expand_range(start: i32, end: i32) -> Vec<String> {
|
||||
(start..=end).map(|i| i.to_string()).collect()
|
||||
}
|
||||
|
||||
for hostname in brace_expand(expr) {
|
||||
result.push(Host {
|
||||
fn expand_list(list: &str) -> Vec<String> {
|
||||
list.split(',').map(|s| s.to_string()).collect()
|
||||
}
|
||||
|
||||
fn expand_string(s: &str) -> Vec<Host> {
|
||||
let mut hosts: Vec<Host> = Vec::new();
|
||||
let mut result = vec![s.to_string()];
|
||||
|
||||
while let Some(r) = result.iter().find(|s| s.contains('[')) {
|
||||
let r = r.clone();
|
||||
let start = r.find('[').unwrap();
|
||||
let end = r.find(']').unwrap();
|
||||
let colon = r.find(':').unwrap();
|
||||
let low = r[start+1..colon].parse::<i32>().unwrap();
|
||||
let high = r[colon+1..end].parse::<i32>().unwrap();
|
||||
result.retain(|s| s != &r);
|
||||
for val in expand_range(low, high) {
|
||||
let new_str = format!("{}{}{}", &r[..start], val, &r[end+1..]);
|
||||
result.push(new_str);
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(r) = result.iter().find(|s| s.contains('{')) {
|
||||
let r = r.clone();
|
||||
let start = r.find('{').unwrap();
|
||||
let end = r.find('}').unwrap();
|
||||
let list = &r[start+1..end];
|
||||
result.retain(|s| s != &r);
|
||||
for val in expand_list(list) {
|
||||
let new_str = format!("{}{}{}", &r[..start], val, &r[end+1..]);
|
||||
result.push(new_str);
|
||||
}
|
||||
}
|
||||
|
||||
for hostname in result {
|
||||
hosts.push(Host {
|
||||
name: hostname.to_string(),
|
||||
ip: None,
|
||||
})
|
||||
}
|
||||
result
|
||||
hosts
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
env_logger::Builder::from_env(Env::default().default_filter_or("info"))
|
||||
.format_timestamp(None)
|
||||
.format_target(false)
|
||||
@ -112,7 +150,7 @@ fn main() {
|
||||
.collect()
|
||||
} else {
|
||||
info!("Using string expansion to build server list.");
|
||||
expand_expression(&args.expression)
|
||||
expand_string(&args.expression)
|
||||
};
|
||||
|
||||
// Dedup hosts from known_hosts file
|
||||
@ -204,4 +242,5 @@ fn main() {
|
||||
} else {
|
||||
warn!("Stopped");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
Reference in New Issue
Block a user