Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 12 additions & 47 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
[profile.release]
strip = true
opt-level = "z"
codegen-units = 1
lto = true

[package]
name = "hostctl"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
hostname = "0.3.1"
libc = "0.2.153"
env_logger = "0.11.3"
clap = { version = "4.5.4", features = ["derive"] }
clap_complete = "4.2"
colored = "2.1.0"
inline_colorization = "0.1.6"
serde_json = "1.0.115"
regex = "1.10.4"
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ Invoke hostctl to execute commands or scripts on the specified hosts.
cd ${INSTALL_DIR?The installation base directory}/hostctl
git clone https://github.com/scoopex/hostctl.git hostctl
cd hostctl
cargo build --release
ln -snf ${INSTALL_DIR?The installation base directory}/hostctl/target/release/hostctl ~/bin/hostctl
cargo build
ln -snf ${INSTALL_DIR?The installation base directory}/hostctl/target/debug/hostctl ~/bin/hostctl
```
3. Build completions
```
target/release/hostctl generate-completions bash > misc/hostctl_bash_completion.sh
target/release/hostctl generate-completions zsh > misc/hostctl_zsh_completion.sh
target/release/hostctl generate-completions fish > misc/hostctl_fish_completion.sh
hostctl generate-completions bash > misc/hostctl_bash_completion.sh
hostctl generate-completions zsh > misc/hostctl_zsh_completion.sh
hostctl generate-completions fish > misc/hostctl_fish_completion.sh
echo "source $INSTALLDIR/hostctl/misc/hostctl_bash_completion.sh" >> .bashrc
exec bash
```
Expand Down
9 changes: 4 additions & 5 deletions hostctl.conf
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#<perl-regex for visibility> : <groupname> : <host>, <host>, ...
foobar-l01-(ap|db)\d+ : web1 : foobar-l01-ap01, foobar-l01-ap02, foobar-l01-ap03, foobar-l01-ap04, foobar-l01-ap05, foobar-l01-ap06
foobar-l01-(ap|db)\d+ : db1 : foobar-l01-db01, foobar-l01-db02, foobar-l01-db03

jump-barfoo : web2 : barfoo-l01-ap01, barfoo-l01-ap02, barfoo-l01-ap03, barfoo-l01-ap04, barfoo-l01-ap05, barfoo-l01-ap06
jump-barfoo : db2 : barfoo-l01-db01, barfoo-l01-db02, barfoo-l01-db03
#foobar-l01-(ap|db)\d+ : web1 : foobar-l01-ap01, foobar-l01-ap02, foobar-l01-ap03, foobar-l01-ap04, foobar-l01-ap05, foobar-l01-ap06
#foobar-l01-(ap|db)\d+ : db1 : foobar-l01-db01, foobar-l01-db02, foobar-l01-db03
#jump-barfoo : web2 : barfoo-l01-ap01, barfoo-l01-ap02, barfoo-l01-ap03, barfoo-l01-ap04, barfoo-l01-ap05, barfoo-l01-ap06
#jump-barfoo : db2 : barfoo-l01-db01, barfoo-l01-db02, barfoo-l01-db03

2 changes: 1 addition & 1 deletion misc/hostctl_bash_completion.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ _hostctl() {

case "${cmd}" in
hostctl)
opts="-c -e -r -n -s -a -j -d -f -q -b -i -o -l -t -w -p -m -h -V --command --execute-local --recipe --nodes --show --array --json --debug --forcecolor --quiet --batchmode --inscreen --optssh --login --term --wait --prompt --makeselection --log-level --help --version [ITEMS]..."
opts="-c -e -r -n -s -a -j -d -f -q -b -i -o -l -t -w -p -m -h -V --command --execute-local --recipe --nodes --show --for-completion --array --json --debug --forcecolor --quiet --batchmode --inscreen --optssh --login --sudo --term --wait --prompt --makeselection --log-level --help --version [ITEM]..."
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
Expand Down
27 changes: 21 additions & 6 deletions src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ fn establish_screen_session(args: &CommandLineArgs){
.args(["-t", "init", "-S", &*args.inscreen, "-d", "-m", "sleep", "120"])
.output()
.expect("failed to start base screen");
output(format!("Wait for screen session for {} seconds", num), OutputType::Error);
output(format!("Wait for screen session for {} seconds", num), OutputType::Info);
thread::sleep(Duration::from_secs(1));
},
1 => {
Expand Down Expand Up @@ -89,8 +89,6 @@ fn establish_base_command(args: &CommandLineArgs, base_executable: &str, node: &
if COUNTER.fetch_add(1, Ordering::Relaxed) == 0 {
establish_screen_session(args);
}
output(format!("NOTE: command were execute in a screen session, attach by executing 'screen -x {}'", args.inscreen), OutputType::Info);
output_str("(see 'man screen' or 'STRG + a :help' for getting information about handling screen sessions)", OutputType::Info);

cmd = Command::new("screen");
cmd.args(["-x", &*args.inscreen, "-m", "-X", "screen", "-t", node]);
Expand Down Expand Up @@ -185,7 +183,6 @@ fn execute_remote(node: String, templated_lines: Vec<String>, args: &CommandLine
}

output(format!("Execute remote : {:?}", cmd), OutputType::Detail);
//output(format!("Execute remote : {:?}", cmd.to_string()), OutputType::Detail);
if args.term {
cmd.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
Expand Down Expand Up @@ -216,10 +213,10 @@ fn execute_remote(node: String, templated_lines: Vec<String>, args: &CommandLine
let status = child.wait().expect("Failed to wait for script");
if !status.success() {
output(format!("FAILED, EXITCODE WAS : {}\n", status.code().unwrap()), OutputType::Error);
false;
return false;
}
output("\nSUCCESS\n".to_string(), OutputType::Info);
true
return true;
}

#[derive(Debug, PartialEq)]
Expand Down Expand Up @@ -300,6 +297,7 @@ pub fn execute_nodes(nodes: Vec<String>, only_nodes: bool, execute_local: bool,
let number_of_nodes = nodes.len();
let mut number_of_current = 0;
let mut failed_nodes: Vec<String> = Vec::new();
let mut successful_nodes: Vec<String> = Vec::new();

if nodes.len() == 0 {
output_str("EXIT: No nodes were specified", OutputType::Fatal);
Expand All @@ -321,6 +319,7 @@ pub fn execute_nodes(nodes: Vec<String>, only_nodes: bool, execute_local: bool,
match res {
NodeResult::Failed => { failed_nodes.push(node.clone());},
NodeResult::Quit => {failed_nodes.push(node.clone()); break 'node_loop},
NodeResult::Ok => {successful_nodes.push(node.clone());},
_ => {}
}
if args.wait > 0 {
Expand All @@ -346,12 +345,28 @@ pub fn execute_nodes(nodes: Vec<String>, only_nodes: bool, execute_local: bool,
}
}

if args.inscreen != "" {
output_str("NOTE: Teardown 'init' screen now", OutputType::Detail);
Command::new("screen")
.args(["-x", &*args.inscreen,"-p","init", "-X","kill"])
.output()
.expect("Failed to teadown 'init' screen");

output(format!("NOTE: command were executed in a screen session, attach to the screen session by executing 'screen -x {}'", args.inscreen), OutputType::Info);
output_str("(see 'man screen' or 'STRG + a :help' for getting information about handling screen sessions)", OutputType::Info);

}

failed_nodes.sort();
failed_nodes.dedup();

if failed_nodes.len() > 0 {
let failed_nodes_str = failed_nodes.join(", ");
output(format!("\n\nCOMPLETED - ONE OR MORE NODES FAILED!\n\nFAILED NODES: {failed_nodes_str}"), OutputType::Error);
if successful_nodes.len() > 0{
let successful_nodes_str = failed_nodes.join(", ");
output(format!("SUCCESSFUL NODES: {successful_nodes_str}"), OutputType::Error);
}
} else {
output_str("\n\nCOMPLETED - ALL NODES WERE SUCCESSFUL", OutputType::Info);
}
Expand Down
7 changes: 7 additions & 0 deletions src/groups_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ pub fn unified_node_list(items: Vec<String>) -> Vec<String> {
sorted_vec
}

pub fn dump_groups_for_completion(){
let groups_map = get_groups_and_nodes(vec!["all".to_string()]);
for (group_name, _) in groups_map.iter() {
println!("{}", group_name);
}
}

pub fn dump_groups(items: Vec<String>, json: bool) {
let groups_map = get_groups_and_nodes(items);
if json {
Expand Down
35 changes: 18 additions & 17 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,25 @@ use crate::utils::{dump_recipes, output_str, OutputType};
use std::env;
use std::io;

fn generate_completions<G: Generator>(shell: G) {
let mut cmd = CommandLineArgs::command();
let bin_name = env!("CARGO_PKG_NAME");
generate(shell, &mut cmd, bin_name, &mut io::stdout());
fn shell_completions(){
if let Some(shell) = env::args().nth(1) {
if shell == "generate-completions" {
if let Some(shell_name) = env::args().nth(2) {
let shell_enum = shell_name.parse::<Shell>();
if let Ok(shell_enum) = shell_enum {
let mut cmd = CommandLineArgs::command();
let bin_name = env!("CARGO_PKG_NAME");
generate(shell_enum, &mut cmd, bin_name, &mut io::stdout());
exit(0);
}
}
eprintln!("Usage: {} generate-completions <shell>", env!("CARGO_PKG_NAME"));
exit(1);
}
}
}


fn main() {
unsafe { libc::umask(0o077) };

Expand All @@ -31,19 +44,7 @@ fn main() {

let mut cli = CommandLineArgs::parse();

if let Some(shell) = env::args().nth(1) {
if shell == "generate-completions" {
if let Some(shell_name) = env::args().nth(2) {
let shell_enum = shell_name.parse::<Shell>();
if let Ok(shell_enum) = shell_enum {
generate_completions(shell_enum);
return;
}
}
eprintln!("Usage: {} generate-completions <shell>", env!("CARGO_PKG_NAME"));
return;
}
}
shell_completions();

env_logger::Builder::from_env(
Env::default().default_filter_or(cli.log_level.clone())
Expand Down
10 changes: 7 additions & 3 deletions src/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub struct CommandLineArgs {
/// Recipes can be stored in $HOME/.hostctl/recipe/ or <hostctl-installation-path>/recipe/ and can
/// be called by their basename.
/// Alternatively, recipes can be called be their fully qualified path.
#[arg(short, long, default_value = "")]
#[arg(short, long, default_value = "", value_hint = clap::ValueHint::AnyPath)]
pub(crate) recipe: String,

/// Specify hosts instead of groups.
Expand All @@ -70,6 +70,10 @@ pub struct CommandLineArgs {
#[arg(short, long)]
pub(crate) show: bool,

/// output for shell completion
#[arg(long)]
pub(crate) for_completion: bool,

/// Output an array list od nodes
#[arg(short, long)]
pub(crate) array: bool,
Expand Down Expand Up @@ -135,6 +139,6 @@ pub struct CommandLineArgs {
pub(crate) log_level: String,

/// Groups or nodes for the iteration
#[arg()]
#[arg(value_name = "ITEM")]
pub(crate) items: Vec<String>,
}
}
Loading