Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Version 0.0.3] Updates to Config Errors, Add Workflows, Default Configs #1

Merged
merged 20 commits into from
Dec 16, 2023
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
32 changes: 32 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Lint & Test

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
test:
name: Run Tests
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Set up Rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
components: rustfmt, clippy
override: true

- name: Run Clippy (Linter)
run: cargo clippy -- -D warnings

- name: Check Code Formatting
run: cargo fmt -- --check

- name: Run Tests
run: cargo test --verbose
2 changes: 1 addition & 1 deletion Cargo.lock

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

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[package]
name = "blaze-ssh"
version = "0.0.2"
version = "0.0.3"
edition = "2021"
authors = ["Sreedev Kodichath <sreedevpadmakumar@gmail.com>"]
description = "A Configurable CLI tool that helps you ssh into aws ec2 instances without leaving the terminal"
rust-version = "1.74.0"
rust-version = "1.73.0"
repository = "https://github.com/sreedevk/blaze-ssh"
readme = "README.md"
keywords = ["aws", "ssh", "ec2", "terminal", "cli"]
Expand Down
22 changes: 15 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,22 @@ Currently, the only way to install blaze is using cargo:
$ cargo install blaze-ssh
```

After installation, make sure to create a config file at `~/.config/blaze/config.toml`. See [Configuration](#configuration) for more details.
You may use the `configure` command to generate a default config file.

```bash
$ blssh configure
```

## Usage
```bash
# CLI Help
Usage: blssh [OPTIONS] <COMMAND>

Commands:
connect connect to an ec2 instances
list list filtered ec2 instances
help Print this message or the help of the given subcommand(s)
connect connect to an ec2 instances
list list filtered ec2 instances
configure generate default config (~/.config/blssh/config.toml)
help Print this message or the help of the given subcommand(s)

Options:
--no-cache disable using cached ec2 instances list
Expand Down Expand Up @@ -57,7 +64,7 @@ Currently, blaze-ssh expects to find a config file at `~/.config/blaze/config.to
[config]
private-key = "~/.ssh/id_rsa.pem"
default-user = "ec2-user"
bastion = "nil"
jumphost = ""
port = 22
address-type = "private"
```
Expand Down Expand Up @@ -85,8 +92,8 @@ $ blssh connect production-1 --user ubuntu
# Connecting with a non default (configured in ~/.config/blaze/config.toml) port
$ blssh connect production-1 --port 2222

# Connecting with a non default bastion host
$ blssh connect production-1 --bastion "user@192.168.1.1"
# Connecting with a non default jumphost host
$ blssh connect production-1 --jumphost "user@192.168.1.1"

# Connecting with a non default (configured in ~/.config/blaze/config.toml) address type
# Options are "public" & "private"
Expand All @@ -106,3 +113,4 @@ $ blssh --no-cache connect production-1
# Roadmap
1. Fix known Issues
2. Package application for distribution
3. If only one instance is found, connect to it directly without showing the connect ui [PR #1](https://github.com/sreedevk/blaze-ssh/pull/1)
220 changes: 199 additions & 21 deletions src/cmdgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl CommandGenerator {
String::from("ssh -t"),
format!("{}@{}", self.user()?, self.address()?),
self.key()?,
self.jump_host()?,
self.jumphost()?,
]
.into_iter()
.filter(|arg| !arg.is_empty())
Expand All @@ -39,10 +39,16 @@ impl CommandGenerator {
.stdout(std::process::Stdio::inherit()))
}

fn jump_host(&self) -> Result<String> {
fn jumphost(&self) -> Result<String> {
match self.opts.jumphost.clone() {
Some(jumphost) => Ok(format!("-J {}", jumphost)),
None => Ok(String::new()),
None => match self.config.jumphost.clone() {
Some(jumphost) => match jumphost.as_str() {
"" => Ok(String::new()),
jmp => Ok(format!("-J {}", jmp)),
},
None => Ok(String::new()),
},
}
}

Expand All @@ -56,32 +62,204 @@ impl CommandGenerator {
}

fn address(&self) -> Result<String> {
let address_type = self
.opts
.address_type
.clone()
.or(self.config.address_type.clone());

match address_type {
Some(address_type) => match address_type.as_str() {
match self.opts.address_type.clone().unwrap_or_default().as_str() {
"" => match self.config.address_type.clone() {
Some(address_type) => match address_type.as_str() {
"public" => Ok(self.instance.public_ip.clone().unwrap_or_default()),
"private" => Ok(self.instance.private_ip.clone().unwrap_or_default()),
_ => Err(anyhow::anyhow!("Invalid address type")),
},
None => Ok(self.instance.private_ip.clone().unwrap_or_default()),
},
address_type => match address_type {
"public" => Ok(self.instance.public_ip.clone().unwrap_or_default()),
"private" => Ok(self.instance.private_ip.clone().unwrap_or_default()),
_ => Err(anyhow::anyhow!("Invalid address type")),
},
None => Ok(self.instance.private_ip.clone().unwrap_or_default()),
}
}

fn user(&self) -> Result<String> {
let username = self
.opts
.user
.clone()
.or(Some(self.config.default_user.clone().unwrap()));

match username {
Some(username) => Ok(username),
None => Err(anyhow::anyhow!("No username provided")),
match self.opts.user.clone().unwrap_or_default().as_str() {
"" => match self.config.default_user.clone() {
Some(default_user) => Ok(default_user.to_string()),
None => Err(anyhow::anyhow!("No username provided. Please use --user or configure default username in ~/.config/blssh/config.toml")),
},
username => Ok(username.to_string()),
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn jumphost_without_opt_uses_config_opt() {
let config = Config {
default_user: None,
private_key: None,
jumphost: Some(String::from("config-jumphost")),
port: None,
address_type: None,
};
let opts = ConnectOptions {
user: None,
key: None,
jumphost: None,
address_type: None,
port: None,
search: None,
};
let instance = InstanceDetails {
instance_id: Some(String::from("id")),
instance_name: Some(String::from("name")),
public_ip: None,
private_ip: None,
};

let command_generator = CommandGenerator::new(&opts, config, instance).unwrap();
assert_eq!(command_generator.jumphost().unwrap(), "-J config-jumphost");
}

#[test]
fn jumphost_with_opt_prioritizes_opt() {
let config = Config {
default_user: None,
private_key: None,
jumphost: Some(String::from("config-jumphost")),
port: None,
address_type: None,
};
let opts = ConnectOptions {
user: None,
key: None,
jumphost: Some(String::from("opt-jumphost")),
address_type: None,
port: None,
search: None,
};
let instance = InstanceDetails {
instance_id: Some(String::from("id")),
instance_name: Some(String::from("name")),
public_ip: None,
private_ip: None,
};

let command_generator = CommandGenerator::new(&opts, config, instance).unwrap();
assert_eq!(command_generator.jumphost().unwrap(), "-J opt-jumphost");
}

#[test]
fn user_with_opt_prioritizes_opt() {
let config = Config {
default_user: Some(String::from("default-user")),
private_key: None,
jumphost: None,
port: None,
address_type: None,
};
let opts = ConnectOptions {
user: Some(String::from("opt-user")),
key: None,
jumphost: None,
address_type: None,
port: None,
search: None,
};
let instance = InstanceDetails {
instance_id: Some(String::from("id")),
instance_name: Some(String::from("name")),
public_ip: None,
private_ip: None,
};

let command_generator = CommandGenerator::new(&opts, config, instance).unwrap();
assert_eq!(command_generator.user().unwrap(), "opt-user");
}

#[test]
fn user_without_opt_uses_config_opt() {
let config = Config {
default_user: Some(String::from("default-user")),
private_key: None,
jumphost: None,
port: None,
address_type: None,
};
let opts = ConnectOptions {
user: None,
key: None,
jumphost: None,
address_type: None,
port: None,
search: None,
};

let instance = InstanceDetails {
instance_id: Some(String::from("id")),
instance_name: Some(String::from("name")),
public_ip: None,
private_ip: None,
};

let command_generator = CommandGenerator::new(&opts, config, instance).unwrap();
assert_eq!(command_generator.user().unwrap(), "default-user");
}

#[test]
fn address_without_opt_uses_config_opt() {
let config = Config {
default_user: None,
private_key: None,
jumphost: None,
port: None,
address_type: Some(String::from("public")),
};
let opts = ConnectOptions {
user: None,
key: None,
jumphost: None,
address_type: None,
port: None,
search: None,
};
let instance = InstanceDetails {
instance_id: Some(String::from("id")),
instance_name: Some(String::from("name")),
public_ip: Some(String::from("public-ip")),
private_ip: Some(String::from("private-ip")),
};

let command_generator = CommandGenerator::new(&opts, config, instance).unwrap();
assert_eq!(command_generator.address().unwrap(), "public-ip");
}

#[test]
fn address_with_opt_prioritizes_opt() {
let config = Config {
default_user: None,
private_key: None,
jumphost: None,
port: None,
address_type: None,
};
let opts = ConnectOptions {
user: None,
key: None,
jumphost: None,
address_type: Some(String::from("public")),
port: None,
search: None,
};
let instance = InstanceDetails {
instance_id: Some(String::from("id")),
instance_name: Some(String::from("name")),
public_ip: Some(String::from("public-ip")),
private_ip: Some(String::from("private-ip")),
};

let command_generator = CommandGenerator::new(&opts, config, instance).unwrap();
assert_eq!(command_generator.address().unwrap(), "public-ip");
}
}
Loading
Loading